diff --git a/web/src/admin/applications/wizard/ApplicationWizardPageBase.ts b/web/src/admin/applications/wizard/ApplicationWizardPageBase.ts new file mode 100644 index 000000000..c21c3a357 --- /dev/null +++ b/web/src/admin/applications/wizard/ApplicationWizardPageBase.ts @@ -0,0 +1,29 @@ +import { AKElement } from "@goauthentik/elements/Base"; +import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; + +import { consume } from "@lit-labs/context"; +import { state } from "@lit/reactive-element/decorators/state.js"; + +import { styles as AwadStyles } from "./ak-application-wizard-application-details.css"; + +import type { WizardState } from "./ak-application-wizard-context"; +import applicationWizardContext from "./ak-application-wizard-context-name"; + +export class ApplicationWizardPageBase extends CustomEmitterElement(AKElement) { + static get styles() { + return AwadStyles; + } + + @consume({ context: applicationWizardContext, subscribe: true }) + @state() + private wizard!: WizardState; + + dispatchWizardUpdate(update: Partial) { + this.dispatchCustomEvent("ak-wizard-update", { + ...this.wizard, + ...update, + }); + } +} + +export default ApplicationWizardPageBase; diff --git a/web/src/admin/applications/wizard/ak-application-wizard-application-details.css.ts b/web/src/admin/applications/wizard/ak-application-wizard-application-details.css.ts index 141d32dee..698b6cbac 100644 --- a/web/src/admin/applications/wizard/ak-application-wizard-application-details.css.ts +++ b/web/src/admin/applications/wizard/ak-application-wizard-application-details.css.ts @@ -6,6 +6,7 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFForm from "@patternfly/patternfly/components/Form/form.css"; import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css"; +import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; @@ -15,12 +16,16 @@ export const styles = [ PFButton, PFForm, PFAlert, + PFRadio, PFInputGroup, PFFormControl, PFSwitch, css` -select[multiple] { -height: 15em; -} -`, + .pf-c-radio__label { + color: #212427; + } + select[multiple] { + height: 15em; + } + `, ]; diff --git a/web/src/admin/applications/wizard/ak-application-wizard-application-details.ts b/web/src/admin/applications/wizard/ak-application-wizard-application-details.ts index f10cd3006..c7d0f4866 100644 --- a/web/src/admin/applications/wizard/ak-application-wizard-application-details.ts +++ b/web/src/admin/applications/wizard/ak-application-wizard-application-details.ts @@ -3,39 +3,22 @@ import { first } from "@goauthentik/common/utils"; import "@goauthentik/components/ak-radio-input"; import "@goauthentik/components/ak-switch-input"; import "@goauthentik/components/ak-text-input"; -import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; -import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; -import { consume } from "@lit-labs/context"; import { msg } from "@lit/localize"; import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { state } from "@lit/reactive-element/decorators/state.js"; import { TemplateResult, html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; -import { styles as AwadStyles } from "./ak-application-wizard-application-details.css"; - -import type { WizardState } from "./ak-application-wizard-context"; -import applicationWizardContext from "./ak-application-wizard-context-name"; +import ApplicationWizardPageBase from "./ApplicationWizardPageBase"; @customElement("ak-application-wizard-application-details") -export class ApplicationWizardApplicationDetails extends CustomEmitterElement(AKElement) { - static get styles() { - return AwadStyles; - } - - @consume({ context: applicationWizardContext, subscribe: true }) - @state() - private wizard!: WizardState; - +export class ApplicationWizardApplicationDetails extends ApplicationWizardPageBase { handleChange(ev: Event) { const value = ev.target.type === "checkbox" ? ev.target.checked : ev.target.value; - - this.dispatchCustomEvent("ak-wizard-update", { - ...this.wizard, + this.dispatchWizardUpdate({ application: { ...this.wizard.application, [ev.target.name]: value, diff --git a/web/src/admin/applications/wizard/ak-application-wizard-authentication-method-choice.ts b/web/src/admin/applications/wizard/ak-application-wizard-authentication-method-choice.ts new file mode 100644 index 000000000..f544bb5d8 --- /dev/null +++ b/web/src/admin/applications/wizard/ak-application-wizard-authentication-method-choice.ts @@ -0,0 +1,68 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/components/ak-radio-input"; +import "@goauthentik/components/ak-switch-input"; +import "@goauthentik/components/ak-text-input"; +import "@goauthentik/elements/forms/FormGroup"; +import "@goauthentik/elements/forms/FormGroup"; +import "@goauthentik/elements/forms/HorizontalFormElement"; + +import { msg } from "@lit/localize"; +import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; +import { html } from "lit"; +import { state } from "lit/decorators.js"; +import { map } from "lit/directives/map.js"; + +import { ProvidersApi } from "@goauthentik/api"; +import type { TypeCreate } from "@goauthentik/api"; + +import ApplicationWizardPageBase from "./ApplicationWizardPageBase"; + +@customElement("ak-application-wizard-authentication-method-choice") +export class ApplicationWizardAuthenticationMethodChoice extends ApplicationWizardPageBase { + @state() + providerTypes: TypeCreate[] = []; + + constructor() { + super(); + this.handleChoice = this.handleChoice.bind(this); + this.renderProvider = this.renderProvider.bind(this); + new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => { + this.providerTypes = types; + }); + } + + handleChoice(ev: Event) { + this.dispatchWizardUpdate({ providerType: ev.target.value }); + } + + renderProvider(type: Provider) { + // Special case; the SAML-by-import method is handled differently + // prettier-ignore + const model = /^SAML/.test(type.name) && type.modelName === "" + ? "samlimporter" + : type.modelName; + + return html`
+ + + ${type.description} +
`; + } + + render() { + return this.providerTypes.length > 0 + ? html`
+ ${map(this.providerTypes, this.renderProvider)} +
` + : html``; + } +} + +export default ApplicationWizardAuthenticationMethodChoice; diff --git a/web/src/admin/applications/wizard/ak-application-wizard-context.ts b/web/src/admin/applications/wizard/ak-application-wizard-context.ts index 3279cda78..72593d6b0 100644 --- a/web/src/admin/applications/wizard/ak-application-wizard-context.ts +++ b/web/src/admin/applications/wizard/ak-application-wizard-context.ts @@ -29,6 +29,7 @@ type OneOfProvider = export type WizardState = { step: number; + providerType: string; application: Partial; provider: OneOfProvider; }; @@ -42,6 +43,7 @@ export class AkApplicationWizardContext extends CustomListenerElement(LitElement @property({ attribute: false }) wizardState: WizardState = { step: 0, + providerType: "", application: {}, provider: {}, }; diff --git a/web/src/admin/applications/wizard/stories/ak-application-wizard-application-details.stories.ts b/web/src/admin/applications/wizard/stories/ak-application-wizard-application-details.stories.ts index 18beccf20..9e0142842 100644 --- a/web/src/admin/applications/wizard/stories/ak-application-wizard-application-details.stories.ts +++ b/web/src/admin/applications/wizard/stories/ak-application-wizard-application-details.stories.ts @@ -4,9 +4,35 @@ import { TemplateResult, html } from "lit"; import "../ak-application-wizard-application-details"; import AkApplicationWizardApplicationDetails from "../ak-application-wizard-application-details"; +import "../ak-application-wizard-authentication-method-choice"; import "../ak-application-wizard-context"; import "./ak-application-context-display-for-test"; +// prettier-ignore +const providerTypes = [ + ["LDAP Provider", "ldapprovider", + "Allow applications to authenticate against authentik's users using LDAP.", + ], + ["OAuth2/OpenID Provider", "oauth2provider", + "OAuth2 Provider for generic OAuth and OpenID Connect Applications.", + ], + ["Proxy Provider", "proxyprovider", + "Protect applications that don't support any of the other\n Protocols by using a Reverse-Proxy.", + ], + ["Radius Provider", "radiusprovider", + "Allow applications to authenticate against authentik's users using Radius.", + ], + ["SAML Provider", "samlprovider", + "SAML 2.0 Endpoint for applications which support SAML.", + ], + ["SCIM Provider", "scimprovider", + "SCIM 2.0 provider to create users and groups in external applications", + ], + ["SAML Provider from Metadata", "", + "Create a SAML Provider by importing its Metadata.", + ], +].map(([name, model_name, description]) => ({ name, description, model_name })); + const metadata: Meta = { title: "Elements / Application Wizard / Page 1", component: "ak-application-wizard-application-details", @@ -16,6 +42,14 @@ const metadata: Meta = { component: "The first page of the application wizard", }, }, + mockData: [ + { + url: "/api/v3/providers/all/types/", + method: "GET", + status: 200, + response: providerTypes, + }, + ], }, }; @@ -42,3 +76,12 @@ export const PageOne = () => { ` ); }; + +export const PageTwo = () => { + return container( + html` + + + ` + ); +};