* main: (22 commits) lifecycle: fix missing krb5 deps for full testing in image (#11815) translate: Updates for file web/xliff/en.xlf in zh-Hans (#11810) translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#11809) translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#11808) web: bump API Client version (#11807) core: bump goauthentik.io/api/v3 from 3.2024083.12 to 3.2024083.13 (#11806) core: bump ruff from 0.7.0 to 0.7.1 (#11805) core: bump twilio from 9.3.4 to 9.3.5 (#11804) core, web: update translations (#11803) providers/scim: handle no members in group in consistency check (#11801) stages/identification: add captcha to identification stage (#11711) website/docs: improve root page and redirect (#11798) providers/scim: clamp batch size for patch requests (#11797) web/admin: fix missing div in wizard forms (#11794) providers/proxy: fix handling of AUTHENTIK_HOST_BROWSER (#11722) core, web: update translations (#11789) core: bump goauthentik.io/api/v3 from 3.2024083.11 to 3.2024083.12 (#11790) core: bump gssapi from 1.8.3 to 1.9.0 (#11791) web: bump API Client version (#11792) stages/authenticator_validate: autoselect last used 2fa device (#11087) ...
143 lines
5.2 KiB
TypeScript
143 lines
5.2 KiB
TypeScript
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
|
import { AkWizard } from "@goauthentik/components/ak-wizard-main/AkWizard";
|
|
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
|
|
import { ContextProvider } from "@lit/context";
|
|
import { msg } from "@lit/localize";
|
|
import { customElement, state } from "lit/decorators.js";
|
|
|
|
import { ProvidersApi, ProxyMode } from "@goauthentik/api";
|
|
|
|
import { applicationWizardContext, applicationWizardProvidersContext } from "./ContextIdentity";
|
|
import { providerTypeRenderers } from "./auth-method-choice/ak-application-wizard-authentication-method-choice.choices.js";
|
|
import { newSteps } from "./steps";
|
|
import {
|
|
ApplicationStep,
|
|
ApplicationWizardState,
|
|
ApplicationWizardStateUpdate,
|
|
OneOfProvider,
|
|
} from "./types";
|
|
|
|
const freshWizardState = (): ApplicationWizardState => ({
|
|
providerModel: "",
|
|
app: {},
|
|
provider: {},
|
|
errors: {},
|
|
proxyMode: ProxyMode.Proxy,
|
|
});
|
|
|
|
@customElement("ak-application-wizard")
|
|
export class ApplicationWizard extends CustomListenerElement(
|
|
AkWizard<ApplicationWizardStateUpdate, ApplicationStep>,
|
|
) {
|
|
constructor() {
|
|
super(msg("Create With Wizard"), msg("New application"), msg("Create a new application"));
|
|
this.steps = newSteps();
|
|
}
|
|
|
|
/**
|
|
* We're going to be managing the content of the forms by percolating all of the data up to this
|
|
* class, which will ultimately transmit all of it to the server as a transaction. The
|
|
* WizardFramework doesn't know anything about the nature of the data itself; it just forwards
|
|
* valid updates to us. So here we maintain a state object *and* update it so all child
|
|
* components can access the wizard state.
|
|
*
|
|
*/
|
|
@state()
|
|
wizardState: ApplicationWizardState = freshWizardState();
|
|
|
|
wizardStateProvider = new ContextProvider(this, {
|
|
context: applicationWizardContext,
|
|
initialValue: this.wizardState,
|
|
});
|
|
|
|
wizardProviderProvider = new ContextProvider(this, {
|
|
context: applicationWizardProvidersContext,
|
|
initialValue: [],
|
|
});
|
|
|
|
/**
|
|
* One of our steps has multiple display variants, one for each type of service provider. We
|
|
* want to *preserve* a customer's decisions about different providers; never make someone "go
|
|
* back and type it all back in," even if it's probably rare that someone will chose one
|
|
* provider, realize it's the wrong one, and go back to chose a different one, *and then go
|
|
* back*. Nonetheless, strive to *never* lose customer input.
|
|
*
|
|
*/
|
|
providerCache: Map<string, OneOfProvider> = new Map();
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((providerTypes) => {
|
|
const wizardReadyProviders = Object.keys(providerTypeRenderers);
|
|
this.wizardProviderProvider.setValue(
|
|
providerTypes
|
|
.filter((providerType) => wizardReadyProviders.includes(providerType.modelName))
|
|
.map((providerType) => ({
|
|
...providerType,
|
|
renderer: providerTypeRenderers[providerType.modelName],
|
|
})),
|
|
);
|
|
});
|
|
}
|
|
|
|
// And this is where all the special cases go...
|
|
handleUpdate(detail: ApplicationWizardStateUpdate) {
|
|
if (detail.status === "submitted") {
|
|
this.step.valid = true;
|
|
this.requestUpdate();
|
|
return;
|
|
}
|
|
|
|
this.step.valid = this.step.valid || detail.status === "valid";
|
|
const update = detail.update;
|
|
|
|
if (!update) {
|
|
return;
|
|
}
|
|
|
|
// When the providerModel enum changes, retrieve the customer's prior work for *this* wizard
|
|
// session (and only this wizard session) or provide an empty model with a default provider
|
|
// name.
|
|
if (update.providerModel && update.providerModel !== this.wizardState.providerModel) {
|
|
const requestedProvider = this.providerCache.get(update.providerModel) ?? {
|
|
name: `Provider for ${this.wizardState.app.name}`,
|
|
};
|
|
if (this.wizardState.providerModel) {
|
|
this.providerCache.set(this.wizardState.providerModel, this.wizardState.provider);
|
|
}
|
|
update.provider = requestedProvider;
|
|
}
|
|
|
|
this.wizardState = update as ApplicationWizardState;
|
|
this.wizardStateProvider.setValue(this.wizardState);
|
|
this.requestUpdate();
|
|
}
|
|
|
|
close() {
|
|
this.steps = newSteps();
|
|
this.currentStep = 0;
|
|
this.wizardState = freshWizardState();
|
|
this.providerCache = new Map();
|
|
this.wizardStateProvider.setValue(this.wizardState);
|
|
this.frame.value!.open = false;
|
|
}
|
|
|
|
handleNav(stepId: number | undefined) {
|
|
if (stepId === undefined || this.steps[stepId] === undefined) {
|
|
throw new Error(`Attempt to navigate to undefined step: ${stepId}`);
|
|
}
|
|
if (stepId > this.currentStep && !this.step.valid) {
|
|
return;
|
|
}
|
|
this.currentStep = stepId;
|
|
this.requestUpdate();
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ak-application-wizard": ApplicationWizard;
|
|
}
|
|
}
|