diff --git a/web/src/admin/applications/wizard/ApplicationWizardCss.ts b/web/src/admin/applications/wizard/BasePanel.css.ts similarity index 100% rename from web/src/admin/applications/wizard/ApplicationWizardCss.ts rename to web/src/admin/applications/wizard/BasePanel.css.ts diff --git a/web/src/admin/applications/wizard/ApplicationWizardPageBase.ts b/web/src/admin/applications/wizard/BasePanel.ts similarity index 88% rename from web/src/admin/applications/wizard/ApplicationWizardPageBase.ts rename to web/src/admin/applications/wizard/BasePanel.ts index 19db5ecbb..f469a029d 100644 --- a/web/src/admin/applications/wizard/ApplicationWizardPageBase.ts +++ b/web/src/admin/applications/wizard/BasePanel.ts @@ -4,9 +4,10 @@ import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; import { consume } from "@lit-labs/context"; import { query, state } from "@lit/reactive-element/decorators.js"; -import { styles as AwadStyles } from "./ApplicationWizardCss"; -import type { WizardState } from "./ak-application-wizard-context"; +import { styles as AwadStyles } from "./BasePanel.css"; + import { applicationWizardContext } from "./ak-application-wizard-context-name"; +import type { WizardState } from "./types"; export class ApplicationWizardPageBase extends CustomEmitterElement(AKElement) { static get styles() { diff --git a/web/src/admin/applications/wizard/TypeApplicationWizardPage.ts b/web/src/admin/applications/wizard/TypeApplicationWizardPage.ts deleted file mode 100644 index ff55cb67c..000000000 --- a/web/src/admin/applications/wizard/TypeApplicationWizardPage.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -import { TypeCreate } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type") -export class TypeApplicationWizardPage extends WizardPage { - applicationTypes: TypeCreate[] = [ - { - component: "ak-application-wizard-type-oauth", - name: msg("OAuth2/OIDC"), - description: msg("Modern applications, APIs and Single-page applications."), - modelName: "", - }, - { - component: "ak-application-wizard-type-saml", - name: msg("SAML"), - description: msg( - "XML-based SSO standard. Use this if your application only supports SAML.", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-proxy", - name: msg("Proxy"), - description: msg("Legacy applications which don't natively support SSO."), - modelName: "", - }, - { - component: "ak-application-wizard-type-ldap", - name: msg("LDAP"), - description: msg( - "Provide an LDAP interface for applications and users to authenticate against.", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-link", - name: msg("Link"), - description: msg( - "Provide an LDAP interface for applications and users to authenticate against.", - ), - modelName: "", - }, - ]; - - sidebarLabel = () => msg("Authentication method"); - - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - render(): TemplateResult { - return html`
- ${this.applicationTypes.map((type) => { - return html`
- { - this.host.steps = [ - "ak-application-wizard-initial", - "ak-application-wizard-type", - type.component, - ]; - this.host.isValid = true; - }} - /> - - ${type.description} -
`; - })} -
`; - } -} diff --git a/web/src/admin/applications/wizard/ak-application-wizard-context.ts b/web/src/admin/applications/wizard/ak-application-wizard-context.ts deleted file mode 100644 index 0dba5171b..000000000 --- a/web/src/admin/applications/wizard/ak-application-wizard-context.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter"; - -import { provide } from "@lit-labs/context"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { property } from "@lit/reactive-element/decorators/property.js"; -import { LitElement, html } from "lit"; - -import { - Application, - LDAPProvider, - OAuth2Provider, - ProxyProvider, - RadiusProvider, - SAMLProvider, - SCIMProvider, -} from "@goauthentik/api"; - -import applicationWizardContext from "./ak-application-wizard-context-name"; - -// my-context.ts - -type OneOfProvider = - | Partial - | Partial - | Partial - | Partial - | Partial - | Partial; - -export interface WizardState { - step: number; - providerType: string; - application: Partial; - provider: OneOfProvider; -} - -type WizardStateEvent = WizardState & { target?: HTMLInputElement }; - -@customElement("ak-application-wizard-context") -export class AkApplicationWizardContext extends CustomListenerElement(LitElement) { - /** - * Providing a context at the root element - */ - @provide({ context: applicationWizardContext }) - @property({ attribute: false }) - wizardState: WizardState = { - step: 0, - providerType: "", - application: {}, - provider: {}, - }; - - constructor() { - super(); - this.handleUpdate = this.handleUpdate.bind(this); - } - - connectedCallback() { - super.connectedCallback(); - this.addCustomListener("ak-wizard-update", this.handleUpdate); - } - - disconnectedCallback() { - this.removeCustomListener("ak-wizard-update", this.handleUpdate); - super.disconnectedCallback(); - } - - handleUpdate(event: CustomEvent) { - delete event.detail.target; - this.wizardState = event.detail; - } - - render() { - return html``; - } -} - -export default AkApplicationWizardContext; diff --git a/web/src/admin/applications/wizard/ak-application-wizard.ts b/web/src/admin/applications/wizard/ak-application-wizard.ts index b2ac9575a..d5c3beb26 100644 --- a/web/src/admin/applications/wizard/ak-application-wizard.ts +++ b/web/src/admin/applications/wizard/ak-application-wizard.ts @@ -11,12 +11,15 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { steps } from "./ApplicationWizardSteps"; import applicationWizardContext from "./ak-application-wizard-context-name"; +import { steps } from "./steps"; import { WizardState, WizardStateEvent } from "./types"; // my-context.ts +// All this thing is doing is recording the input the user makes to the forms. It should NOT be +// triggering re-renders; that's the wizard frame's jobs. + @customElement("ak-application-wizard") export class ApplicationWizard extends CustomListenerElement(AKElement) { static get styles(): CSSResult[] { @@ -38,19 +41,8 @@ export class ApplicationWizard extends CustomListenerElement(AKElement) { @state() steps = steps; - @property({ type: Boolean }) - open = false; - @property() - createText = msg("Create"); - - @property({ type: Boolean }) - showButton = true; - - @property({ attribute: false }) - finalHandler: () => Promise = () => { - return Promise.resolve(); - }; + prompt = msg("Create"); constructor() { super(); @@ -83,8 +75,6 @@ export class ApplicationWizard extends CustomListenerElement(AKElement) { method.disabled = false; this.steps = newSteps; } - - console.log(newWizardState); this.wizardState = newWizardState; } @@ -94,12 +84,8 @@ export class ApplicationWizard extends CustomListenerElement(AKElement) { .steps=${this.steps} header=${msg("New application")} description=${msg("Create a new application.")} + prompt=${this.prompt} > - ${this.showButton - ? html`` - : html``} `; } diff --git a/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts b/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts index 27e1346b9..4ecdba6c1 100644 --- a/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts +++ b/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts @@ -12,10 +12,10 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.j import { TemplateResult, html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; -import ApplicationWizardPageBase from "../ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; @customElement("ak-application-wizard-application-details") -export class ApplicationWizardApplicationDetails extends ApplicationWizardPageBase { +export class ApplicationWizardApplicationDetails extends BasePanel { handleChange(ev: Event) { if (!ev.target) { console.warn(`Received event with no target: ${ev}`); diff --git a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts index 65c3cce6a..5b64efacb 100644 --- a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts +++ b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts @@ -12,11 +12,11 @@ import { map } from "lit/directives/map.js"; import type { TypeCreate } from "@goauthentik/api"; -import ApplicationWizardPageBase from "../ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; import providerTypesList from "./ak-application-wizard-authentication-method-choice.choices"; @customElement("ak-application-wizard-authentication-method-choice") -export class ApplicationWizardAuthenticationMethodChoice extends ApplicationWizardPageBase { +export class ApplicationWizardAuthenticationMethodChoice extends BasePanel { constructor() { super(); this.handleChoice = this.handleChoice.bind(this); diff --git a/web/src/admin/applications/wizard/design.md b/web/src/admin/applications/wizard/design.md deleted file mode 100644 index 17d44e025..000000000 --- a/web/src/admin/applications/wizard/design.md +++ /dev/null @@ -1,20 +0,0 @@ -The design of the wizard is actually very simple. There is an orchestrator in the Context object; -it takes messages from the current page and grants permissions to proceed based on the content of -the Context object after a message. - -The fields of the Context object are: - -```Javascript -{ - step: number // The page currently being visited - providerType: The provider type chosen in step 2. Dictates which view to show in step 3 - application: // The data collected from the ApplicationDetails page - provider: // the data collected from the ProviderDetails page. - - -``` - -The orchestrator leans on the per-page forms to tell it when a page is "valid enough to proceed". - -When it reaches the last page, the transaction is triggered. If there are errors, the user is -invited to "go back to the page where the error occurred" and try again. diff --git a/web/src/admin/applications/wizard/link/TypeLinkApplicationWizardPage.ts b/web/src/admin/applications/wizard/link/TypeLinkApplicationWizardPage.ts deleted file mode 100644 index c91133a45..000000000 --- a/web/src/admin/applications/wizard/link/TypeLinkApplicationWizardPage.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -@customElement("ak-application-wizard-type-link") -export class TypeLinkApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Application Link"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - this.host.state["link"] = data.link; - return true; - }; - - renderForm(): TemplateResult { - return html` -
- - -

- ${msg("URL which will be opened when a user clicks on the application.")} -

-
-
- `; - } -} diff --git a/web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts b/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts similarity index 77% rename from web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts rename to web/src/admin/applications/wizard/methods/BaseProviderPanel.ts index b5db2e215..2558a3b6d 100644 --- a/web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts +++ b/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts @@ -1,6 +1,6 @@ -import ApplicationWizardPageBase from "./ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; -export class ApplicationWizardProviderPageBase extends ApplicationWizardPageBase { +export class ApplicationWizardProviderPageBase extends BasePanel { handleChange(ev: InputEvent) { if (!ev.target) { console.warn(`Received event with no target: ${ev}`); diff --git a/web/src/admin/applications/wizard/auth-method/ak-application-wizard-authentication-method.ts b/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts similarity index 59% rename from web/src/admin/applications/wizard/auth-method/ak-application-wizard-authentication-method.ts rename to web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts index ab727ba07..b63741326 100644 --- a/web/src/admin/applications/wizard/auth-method/ak-application-wizard-authentication-method.ts +++ b/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts @@ -1,16 +1,16 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import ApplicationWizardPageBase from "../ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; import { providerRendererList } from "../auth-method-choice/ak-application-wizard-authentication-method-choice.choices"; -import "../ldap/ak-application-wizard-authentication-by-ldap"; -import "../oauth/ak-application-wizard-authentication-by-oauth"; -import "../proxy/ak-application-wizard-authentication-for-reverse-proxy"; -import "../proxy/ak-application-wizard-authentication-for-single-forward-proxy"; +import "./ldap/ak-application-wizard-authentication-by-ldap"; +import "./oauth/ak-application-wizard-authentication-by-oauth"; +import "./proxy/ak-application-wizard-authentication-for-reverse-proxy"; +import "./proxy/ak-application-wizard-authentication-for-single-forward-proxy"; // prettier-ignore @customElement("ak-application-wizard-authentication-method") -export class ApplicationWizardApplicationDetails extends ApplicationWizardPageBase { +export class ApplicationWizardApplicationDetails extends BasePanel { render() { const handler = providerRendererList.get(this.wizard.providerType); if (!handler) { diff --git a/web/src/admin/applications/wizard/ldap/LDAPOptionsAndHelp.ts b/web/src/admin/applications/wizard/methods/ldap/LDAPOptionsAndHelp.ts similarity index 100% rename from web/src/admin/applications/wizard/ldap/LDAPOptionsAndHelp.ts rename to web/src/admin/applications/wizard/methods/ldap/LDAPOptionsAndHelp.ts diff --git a/web/src/admin/applications/wizard/ldap/ak-application-wizard-authentication-by-ldap.ts b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts similarity index 97% rename from web/src/admin/applications/wizard/ldap/ak-application-wizard-authentication-by-ldap.ts rename to web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts index c9e0d8d0a..7b9aa4d3f 100644 --- a/web/src/admin/applications/wizard/ldap/ak-application-wizard-authentication-by-ldap.ts +++ b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts @@ -18,7 +18,7 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { FlowsInstancesListDesignationEnum } from "@goauthentik/api"; import type { LDAPProvider } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; import { bindModeOptions, cryptoCertificateHelp, @@ -31,7 +31,7 @@ import { } from "./LDAPOptionsAndHelp"; @customElement("ak-application-wizard-authentication-by-ldap") -export class ApplicationWizardApplicationDetails extends ApplicationWizardProviderPageBase { +export class ApplicationWizardApplicationDetails extends BaseProviderPanel { render() { const provider = this.wizard.provider as LDAPProvider | undefined; diff --git a/web/src/admin/applications/wizard/oauth/ak-application-wizard-authentication-by-oauth.ts b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts similarity index 98% rename from web/src/admin/applications/wizard/oauth/ak-application-wizard-authentication-by-oauth.ts rename to web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts index 02bd54ac5..cf6ceb9de 100644 --- a/web/src/admin/applications/wizard/oauth/ak-application-wizard-authentication-by-oauth.ts +++ b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts @@ -33,10 +33,10 @@ import type { PaginatedScopeMappingList, } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; @customElement("ak-application-wizard-authentication-by-oauth") -export class ApplicationWizardAuthenticationByOauth extends ApplicationWizardProviderPageBase { +export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel { @state() showClientSecret = false; diff --git a/web/src/admin/applications/wizard/proxy/AuthenticationByProxyPage.ts b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts similarity index 98% rename from web/src/admin/applications/wizard/proxy/AuthenticationByProxyPage.ts rename to web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts index 1f7ecdf2e..c3ac7f44f 100644 --- a/web/src/admin/applications/wizard/proxy/AuthenticationByProxyPage.ts +++ b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts @@ -21,11 +21,11 @@ import { SourcesApi, } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; type MaybeTemplateResult = TemplateResult | typeof nothing; -export class AkTypeProxyApplicationWizardPage extends ApplicationWizardProviderPageBase { +export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel { constructor() { super(); new PropertymappingsApi(DEFAULT_CONFIG) diff --git a/web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts similarity index 100% rename from web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts rename to web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts diff --git a/web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts similarity index 100% rename from web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts rename to web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts diff --git a/web/src/admin/applications/wizard/saml/SamlProviderOptions.ts b/web/src/admin/applications/wizard/methods/saml/SamlProviderOptions.ts similarity index 100% rename from web/src/admin/applications/wizard/saml/SamlProviderOptions.ts rename to web/src/admin/applications/wizard/methods/saml/SamlProviderOptions.ts diff --git a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-configuration.ts b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts similarity index 98% rename from web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-configuration.ts rename to web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts index 2313749c3..f25c995ef 100644 --- a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-configuration.ts +++ b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts @@ -21,7 +21,7 @@ import { SAMLProvider, } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; import { digestAlgorithmOptions, signatureAlgorithmOptions, @@ -29,7 +29,7 @@ import { } from "./SamlProviderOptions"; @customElement("ak-application-wizard-authentication-by-saml-configuration") -export class ApplicationWizardProviderSamlConfiguration extends ApplicationWizardProviderPageBase { +export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPanel { propertyMappings?: PaginatedSAMLPropertyMappingList; constructor() { diff --git a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-import.ts b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts similarity index 89% rename from web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-import.ts rename to web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts index 49d03ef7f..dda096a67 100644 --- a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-import.ts +++ b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts @@ -9,10 +9,10 @@ import { html } from "lit"; import { FlowsInstancesListDesignationEnum } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; @customElement("ak-application-wizard-authentication-by-saml-import") -export class ApplicationWizardProviderSamlImport extends ApplicationWizardProviderPageBase { +export class ApplicationWizardProviderSamlImport extends BaseProviderPanel { render() { return html`
diff --git a/web/src/admin/applications/wizard/saml/saml-property-mappings-search.ts b/web/src/admin/applications/wizard/methods/saml/saml-property-mappings-search.ts similarity index 100% rename from web/src/admin/applications/wizard/saml/saml-property-mappings-search.ts rename to web/src/admin/applications/wizard/methods/saml/saml-property-mappings-search.ts diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthAPIApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthAPIApplicationWizardPage.ts deleted file mode 100644 index a2e08c2aa..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthAPIApplicationWizardPage.ts +++ /dev/null @@ -1,35 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -@customElement("ak-application-wizard-type-oauth-api") -export class TypeOAuthAPIApplicationWizardPage extends WizardPage { - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - sidebarLabel = () => msg("Method details"); - - render(): TemplateResult { - return html` -

- ${msg( - "This configuration can be used to authenticate to authentik with other APIs other otherwise programmatically.", - )} -

-

- ${msg( - "By default, all service accounts can authenticate as this application, as long as they have a valid token of the type app-password.", - )} -

-
`; - } -} diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthApplicationWizardPage.ts deleted file mode 100644 index bb19272e9..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthApplicationWizardPage.ts +++ /dev/null @@ -1,84 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -import { TypeCreate } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-oauth") -export class TypeOAuthApplicationWizardPage extends WizardPage { - applicationTypes: TypeCreate[] = [ - { - component: "ak-application-wizard-type-oauth-code", - name: msg("Web application"), - description: msg( - "Applications which handle the authentication server-side (for example, Python, Go, Rust, Java, PHP)", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-oauth-implicit", - name: msg("Single-page applications"), - description: msg( - "Single-page applications which handle authentication in the browser (for example, Javascript, Angular, React, Vue)", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-oauth-implicit", - name: msg("Native application"), - description: msg( - "Applications which redirect users to a non-web callback (for example, Android, iOS)", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-oauth-api", - name: msg("API"), - description: msg( - "Authentication without user interaction, or machine-to-machine authentication.", - ), - modelName: "", - }, - ]; - - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - sidebarLabel = () => msg("Application type"); - - render(): TemplateResult { - return html`
- ${this.applicationTypes.map((type) => { - return html`
- { - this.host.steps = [ - "ak-application-wizard-initial", - "ak-application-wizard-type", - "ak-application-wizard-type-oauth", - type.component, - ]; - this.host.state["oauth-type"] = type.component; - this.host.isValid = true; - }} - /> - - ${type.description} -
`; - })} -
`; - } -} diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthCodeApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthCodeApplicationWizardPage.ts deleted file mode 100644 index 458def24b..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthCodeApplicationWizardPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import "@goauthentik/admin/common/ak-flow-search/ak-flow-search-no-default"; -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import "@goauthentik/elements/forms/SearchSelect"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; -import "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { - ClientTypeEnum, - FlowsInstancesListDesignationEnum, - OAuth2ProviderRequest, - ProvidersApi, -} from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-oauth-code") -export class TypeOAuthCodeApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Method details"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - const req: OAuth2ProviderRequest = { - name: this.host.state["name"] as string, - clientType: ClientTypeEnum.Confidential, - authorizationFlow: data.authorizationFlow as string, - }; - const provider = await new ProvidersApi(DEFAULT_CONFIG).providersOauth2Create({ - oAuth2ProviderRequest: req, - }); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - -

- ${msg("Flow used when users access this application.")} -

-
-
`; - } -} diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthImplicitApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthImplicitApplicationWizardPage.ts deleted file mode 100644 index c1fa0f3cf..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthImplicitApplicationWizardPage.ts +++ /dev/null @@ -1,15 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -@customElement("ak-application-wizard-type-oauth-implicit") -export class TypeOAuthImplicitApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Method details"); - - render(): TemplateResult { - return html`
some stuff idk
`; - } -} diff --git a/web/src/admin/applications/wizard/proxy/TypeProxyApplicationWizardPage.ts b/web/src/admin/applications/wizard/proxy/TypeProxyApplicationWizardPage.ts deleted file mode 100644 index 43db7c56c..000000000 --- a/web/src/admin/applications/wizard/proxy/TypeProxyApplicationWizardPage.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { - FlowDesignationEnum, - FlowsApi, - ProvidersApi, - ProxyProviderRequest, -} from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-proxy") -export class TypeProxyApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Proxy details"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - let name = this.host.state["name"] as string; - // Check if a provider with the name already exists - const providers = await new ProvidersApi(DEFAULT_CONFIG).providersAllList({ - search: name, - }); - if (providers.results.filter((provider) => provider.name == name)) { - name += "-1"; - } - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - // Get all flows and default to the implicit authorization - const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ - designation: FlowDesignationEnum.Authorization, - ordering: "slug", - }); - const req: ProxyProviderRequest = { - name: name, - authorizationFlow: flows.results[0].pk, - externalHost: data.externalHost as string, - }; - const provider = await new ProvidersApi(DEFAULT_CONFIG).providersProxyCreate({ - proxyProviderRequest: req, - }); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - -

- ${msg("External domain you will be accessing the domain from.")} -

-
-
`; - } -} diff --git a/web/src/admin/applications/wizard/saml/TypeSAMLApplicationWizardPage.ts b/web/src/admin/applications/wizard/saml/TypeSAMLApplicationWizardPage.ts deleted file mode 100644 index 2ef4d6972..000000000 --- a/web/src/admin/applications/wizard/saml/TypeSAMLApplicationWizardPage.ts +++ /dev/null @@ -1,66 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -import { TypeCreate } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-saml") -export class TypeOAuthApplicationWizardPage extends WizardPage { - applicationTypes: TypeCreate[] = [ - { - component: "ak-application-wizard-type-saml-import", - name: msg("Import SAML Metadata"), - description: msg( - "Import the metadata document of the applicaation you want to configure.", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-saml-config", - name: msg("Manual configuration"), - description: msg("Manually configure SAML"), - modelName: "", - }, - ]; - - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - sidebarLabel = () => msg("Application type"); - - render(): TemplateResult { - return html`
- ${this.applicationTypes.map((type) => { - return html`
- { - this.host.steps = [ - "ak-application-wizard-initial", - "ak-application-wizard-type", - "ak-application-wizard-type-saml", - type.component, - ]; - this.host.state["saml-type"] = type.component; - this.host.isValid = true; - }} - /> - - ${type.description} -
`; - })} -
`; - } -} diff --git a/web/src/admin/applications/wizard/saml/TypeSAMLConfigApplicationWizardPage.ts b/web/src/admin/applications/wizard/saml/TypeSAMLConfigApplicationWizardPage.ts deleted file mode 100644 index ad269ac63..000000000 --- a/web/src/admin/applications/wizard/saml/TypeSAMLConfigApplicationWizardPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { FlowDesignationEnum, FlowsApi, ProvidersApi, SAMLProviderRequest } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-saml-config") -export class TypeSAMLApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("SAML details"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - let name = this.host.state["name"] as string; - // Check if a provider with the name already exists - const providers = await new ProvidersApi(DEFAULT_CONFIG).providersAllList({ - search: name, - }); - if (providers.results.filter((provider) => provider.name == name)) { - name += "-1"; - } - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - // Get all flows and default to the implicit authorization - const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ - designation: FlowDesignationEnum.Authorization, - ordering: "slug", - }); - const req: SAMLProviderRequest = { - name: name, - authorizationFlow: flows.results[0].pk, - acsUrl: data.acsUrl as string, - }; - const provider = await new ProvidersApi(DEFAULT_CONFIG).providersSamlCreate({ - sAMLProviderRequest: req, - }); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - -

- ${msg( - "URL that authentik will redirect back to after successful authentication.", - )} -

-
-
`; - } -} diff --git a/web/src/admin/applications/wizard/saml/TypeSAMLImportApplicationWizardPage.ts b/web/src/admin/applications/wizard/saml/TypeSAMLImportApplicationWizardPage.ts deleted file mode 100644 index c3ddfda79..000000000 --- a/web/src/admin/applications/wizard/saml/TypeSAMLImportApplicationWizardPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { - FlowDesignationEnum, - FlowsApi, - ProvidersApi, - ProvidersSamlImportMetadataCreateRequest, -} from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-saml-import") -export class TypeSAMLImportApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Import SAML metadata"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - let name = this.host.state["name"] as string; - // Check if a provider with the name already exists - const providers = await new ProvidersApi(DEFAULT_CONFIG).providersAllList({ - search: name, - }); - if (providers.results.filter((provider) => provider.name == name)) { - name += "-1"; - } - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - // Get all flows and default to the implicit authorization - const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ - designation: FlowDesignationEnum.Authorization, - ordering: "slug", - }); - const req: ProvidersSamlImportMetadataCreateRequest = { - name: name, - authorizationFlow: flows.results[0].slug, - file: data["metadata"] as Blob, - }; - const provider = await new ProvidersApi( - DEFAULT_CONFIG, - ).providersSamlImportMetadataCreate(req); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - - -
`; - } -} diff --git a/web/src/admin/applications/wizard/ApplicationWizardSteps.ts b/web/src/admin/applications/wizard/steps.ts similarity index 70% rename from web/src/admin/applications/wizard/ApplicationWizardSteps.ts rename to web/src/admin/applications/wizard/steps.ts index 2c720165b..1b7a5a072 100644 --- a/web/src/admin/applications/wizard/ApplicationWizardSteps.ts +++ b/web/src/admin/applications/wizard/steps.ts @@ -1,16 +1,15 @@ -import { WizardStep, makeWizardId } from "@goauthentik/components/ak-wizard-main"; +import { WizardStep } from "@goauthentik/components/ak-wizard-main"; import { msg } from "@lit/localize"; import { html } from "lit"; import "./application/ak-application-wizard-application-details"; import "./auth-method-choice/ak-application-wizard-authentication-method-choice"; -import "./auth-method/ak-application-wizard-authentication-method"; +import "./methods/ak-application-wizard-authentication-method"; export const steps: WizardStep[] = [ { - id: makeWizardId("application"), - nextStep: makeWizardId("auth-method-choice"), + id: "application", label: "Application Details", renderer: () => html``, @@ -19,9 +18,7 @@ export const steps: WizardStep[] = [ valid: true, }, { - id: makeWizardId("auth-method-choice"), - backStep: makeWizardId("application"), - nextStep: makeWizardId("auth-method"), + id: "auth-method-choice", label: "Authentication Method", renderer: () => html``, @@ -31,8 +28,7 @@ export const steps: WizardStep[] = [ valid: true, }, { - id: makeWizardId("auth-method"), - backStep: makeWizardId("auth-method-choice"), + id: "auth-method", label: "Authentication Details", renderer: () => html``, diff --git a/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts b/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts index 3f8e0ce26..6abd7563e 100644 --- a/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts +++ b/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts @@ -3,8 +3,8 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.j import { state } from "@lit/reactive-element/decorators/state.js"; import { LitElement, html } from "lit"; -import type { WizardState } from "../ak-application-wizard-context"; import applicationWizardContext from "../ak-application-wizard-context-name"; +import type { WizardState } from "../types"; @customElement("ak-application-context-display-for-test") export class ApplicationContextDisplayForTest extends LitElement { diff --git a/web/src/components/ak-wizard-main/ak-wizard-frame.ts b/web/src/components/ak-wizard-main/ak-wizard-frame.ts index 6368a9010..712e9c777 100644 --- a/web/src/components/ak-wizard-main/ak-wizard-frame.ts +++ b/web/src/components/ak-wizard-main/ak-wizard-frame.ts @@ -63,6 +63,20 @@ export class AkWizardFrame extends CustomEmitterElement(ModalButton) { this.open = false; } + get maxStep() { + return this.steps.length - 1; + } + + get nextStep() { + const idx = this.steps.findIndex((step) => step === this.currentStep); + return idx < this.maxStep ? this.steps[idx + 1] : undefined; + } + + get backStep() { + const idx = this.steps.findIndex((step) => step === this.currentStep); + return idx > 0 ? this.steps[idx - 1] : undefined; + } + renderModalInner() { // prettier-ignore return html`
@@ -134,32 +148,30 @@ export class AkWizardFrame extends CustomEmitterElement(ModalButton) { renderFooter() { return html`
- ${this.currentStep.nextStep ? this.renderFooterNextButton() : nothing} - ${this.currentStep.backStep ? this.renderFooterBackButton() : nothing} + ${this.nextStep ? this.renderFooterNextButton(this.nextStep) : nothing} + ${this.backStep ? this.renderFooterBackButton(this.backStep) : nothing} ${this.canCancel ? this.renderFooterCancelButton() : nothing}
`; } - renderFooterNextButton() { + renderFooterNextButton(nextStep: WizardStep) { return html``; } - renderFooterBackButton() { + renderFooterBackButton(backStep: WizardStep) { return html` diff --git a/web/src/components/ak-wizard-main/ak-wizard-main.ts b/web/src/components/ak-wizard-main/ak-wizard-main.ts index 80f1c15d0..f4be5734a 100644 --- a/web/src/components/ak-wizard-main/ak-wizard-main.ts +++ b/web/src/components/ak-wizard-main/ak-wizard-main.ts @@ -20,8 +20,11 @@ import type { WizardStep } from "./types"; * * @element ak-wizard-main * - * This is the entry point for the wizard. - * + * This is the entry point for the wizard. Its tasks are: + * - keep the collection of steps + * - maintain the open/close status of the modal + * - listens for navigation events + * - if a navigation event is valid, switch to the panel requested */ @customElement("ak-wizard-main") diff --git a/web/src/components/ak-wizard-main/index.ts b/web/src/components/ak-wizard-main/index.ts index 30bb39ff1..572b3befe 100644 --- a/web/src/components/ak-wizard-main/index.ts +++ b/web/src/components/ak-wizard-main/index.ts @@ -1,5 +1,4 @@ import "./ak-wizard-main"; -import type { WizardStep, WizardStepId } from "./types"; -import { makeWizardId } from "./types"; +import type { WizardStep } from "./types"; -export { WizardStepId, WizardStep, makeWizardId }; +export { WizardStep }; diff --git a/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts b/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts index 235f55faf..e35f9de5e 100644 --- a/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts +++ b/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts @@ -6,7 +6,6 @@ import { TemplateResult, html } from "lit"; import "../ak-wizard-main"; import AkWizard from "../ak-wizard-main"; import type { WizardStep } from "../types"; -import { makeWizardId } from "../types"; const metadata: Meta = { title: "Components / Wizard / Basic", @@ -38,22 +37,20 @@ const container = (testItem: TemplateResult) => const dummySteps: WizardStep[] = [ { - id: makeWizardId("0"), + id: "0", label: "Test Step1", renderer: () => html`

This space intentionally left blank today

`, disabled: false, valid: true, - nextStep: makeWizardId("1"), nextButtonLabel: "Next", backButtonLabel: undefined, }, { - id: makeWizardId("1"), + id: "1", label: "Test Step 2", renderer: () => html`

This space also intentionally left blank

`, disabled: false, valid: true, - backStep: makeWizardId("0"), nextButtonLabel: undefined, backButtonLabel: "Back", }, diff --git a/web/src/components/ak-wizard-main/types.ts b/web/src/components/ak-wizard-main/types.ts index a1911fb0d..f295a77f4 100644 --- a/web/src/components/ak-wizard-main/types.ts +++ b/web/src/components/ak-wizard-main/types.ts @@ -1,15 +1,7 @@ import { TemplateResult } from "lit"; -type PhantomType = { _type: Type } & Data; - -export type WizardStepId = PhantomType<"WizardId", string>; - -export const makeWizardId = (id: string): WizardStepId => id as WizardStepId; - export interface WizardStep { - id: WizardStepId; - nextStep?: WizardStepId; - backStep?: WizardStepId; + id: string; label: string; valid: boolean; renderer: () => TemplateResult;