diff --git a/web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts b/web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts
new file mode 100644
index 000000000..b5db2e215
--- /dev/null
+++ b/web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts
@@ -0,0 +1,20 @@
+import ApplicationWizardPageBase from "./ApplicationWizardPageBase";
+
+export class ApplicationWizardProviderPageBase extends ApplicationWizardPageBase {
+ handleChange(ev: InputEvent) {
+ if (!ev.target) {
+ console.warn(`Received event with no target: ${ev}`);
+ return;
+ }
+ const target = ev.target as HTMLInputElement;
+ const value = target.type === "checkbox" ? target.checked : target.value;
+ this.dispatchWizardUpdate({
+ provider: {
+ ...this.wizard.provider,
+ [target.name]: value,
+ },
+ });
+ }
+}
+
+export default ApplicationWizardProviderPageBase;
diff --git a/web/src/admin/applications/wizard/ak-application-wizard-authentication-method-choice.choices.ts b/web/src/admin/applications/wizard/ak-application-wizard-authentication-method-choice.choices.ts
index 46e33aab8..c3d8669ee 100644
--- a/web/src/admin/applications/wizard/ak-application-wizard-authentication-method-choice.choices.ts
+++ b/web/src/admin/applications/wizard/ak-application-wizard-authentication-method-choice.choices.ts
@@ -1,35 +1,70 @@
import { msg } from "@lit/localize";
+import { TemplateResult, html } from "lit";
import type { TypeCreate } from "@goauthentik/api";
-type ProviderType = [string, string, string] | [string, string, string, ProviderType[]];
+type ProviderRenderer = () => TemplateResult;
-type ProviderOption = TypeCreate & {
- children?: TypeCreate[];
-};
+type ProviderType = [string, string, string, ProviderRenderer];
// prettier-ignore
const _providerTypesTable: ProviderType[] = [
- ["oauth2provider", msg("OAuth2/OpenID"), msg("Modern applications, APIs and Single-page applications.")],
- ["ldapprovider", msg("LDAP"), msg("Provide an LDAP interface for applications and users to authenticate against.")],
- ["proxyprovider-proxy", msg("Transparent Reverse Proxy"), msg("For transparent reverse proxies with required authentication")],
- ["proxyprovider-forwardsingle", msg("Forward Single Proxy"), msg("For nginx's auth_request or traefix's forwardAuth")],
- ["radiusprovider", msg("Radius"), msg("Allow applications to authenticate against authentik's users using Radius.")],
- ["samlprovider-manual", msg("SAML Manual configuration"), msg("Configure SAML provider manually")],
- ["samlprovider-import", msg("SAML Import Configuration"), msg("Create a SAML provider by importing its metadata")],
- ["scimprovider", msg("SCIM Provider"), msg("SCIM 2.0 provider to create users and groups in external applications")]
+ [
+ "oauth2provider",
+ msg("OAuth2/OpenID"),
+ msg("Modern applications, APIs and Single-page applications."),
+ () => html`
Under construction
` + ], + + [ + "samlprovider-import", + msg("SAML Import Configuration"), + msg("Create a SAML provider by importing its metadata"), + () => html`Under construction
` + ], ]; -function mapProviders([modelName, name, description, children]: ProviderType): ProviderOption { +function mapProviders([modelName, name, description]: ProviderType): TypeCreate { return { modelName, name, description, component: "", - ...(children ? { children: children.map(mapProviders) } : {}), }; } export const providerTypesList = _providerTypesTable.map(mapProviders); +export const providerRendererList = new MapUnder construction
`], - ["samlprovider", () => html`Under construction
`], - ["scimprovider", () => html`Under construction
`], -]); @customElement("ak-application-wizard-authentication-method") export class ApplicationWizardApplicationDetails extends ApplicationWizardPageBase { render() { - const handler = handlers.get(this.wizard.providerType); + const handler = providerRendererList.get(this.wizard.providerType); if (!handler) { throw new Error( "Unrecognized authentication method in ak-application-wizard-authentication-method", diff --git a/web/src/admin/applications/wizard/design.md b/web/src/admin/applications/wizard/design.md new file mode 100644 index 000000000..17d44e025 --- /dev/null +++ b/web/src/admin/applications/wizard/design.md @@ -0,0 +1,20 @@ +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/ldap/LDAPOptionsAndHelp.ts b/web/src/admin/applications/wizard/ldap/LDAPOptionsAndHelp.ts new file mode 100644 index 000000000..5b2f1f483 --- /dev/null +++ b/web/src/admin/applications/wizard/ldap/LDAPOptionsAndHelp.ts @@ -0,0 +1,64 @@ +import { msg } from "@lit/localize"; +import { html } from "lit"; + +import { LDAPAPIAccessMode } from "@goauthentik/api"; + +export const bindModeOptions = [ + { + label: msg("Cached binding"), + value: LDAPAPIAccessMode.Cached, + default: true, + description: html`${msg( + "Flow is executed and session is cached in memory. Flow is executed when session expires", + )}`, + }, + { + label: msg("Direct binding"), + value: LDAPAPIAccessMode.Direct, + description: html`${msg( + "Always execute the configured bind flow to authenticate the user", + )}`, + }, +]; + +export const searchModeOptions = [ + { + label: msg("Cached querying"), + value: LDAPAPIAccessMode.Cached, + default: true, + description: html`${msg( + "The outpost holds all users and groups in-memory and will refresh every 5 Minutes", + )}`, + }, + { + label: msg("Direct querying"), + value: LDAPAPIAccessMode.Direct, + description: html`${msg( + "Always returns the latest data, but slower than cached querying", + )}`, + }, +]; + +export const mfaSupportHelp = msg( + "When enabled, code-based multi-factor authentication can be used by appending a semicolon and the TOTP code to the password. This should only be enabled if all users that will bind to this provider have a TOTP device configured, as otherwise a password may incorrectly be rejected if it contains a semicolon.", +); + +export const groupHelp = msg( + "The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber", +); + +export const cryptoCertificateHelp = msg( + "The certificate for the above configured Base DN. As a fallback, the provider uses a self-signed certificate.", +); + +export const tlsServerNameHelp = msg( + "DNS name for which the above configured certificate should be used. The certificate cannot be detected based on the base DN, as the SSL/TLS negotiation happens before such data is exchanged.", +); + +export const uidStartNumberHelp = msg( + "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber", +); + +export const gidStartNumberHelp = msg( + "The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber", +); diff --git a/web/src/admin/applications/wizard/ldap/TypeLDAPApplicationWizardPage.ts b/web/src/admin/applications/wizard/ldap/TypeLDAPApplicationWizardPage.ts deleted file mode 100644 index 89c4a5ec8..000000000 --- a/web/src/admin/applications/wizard/ldap/TypeLDAPApplicationWizardPage.ts +++ /dev/null @@ -1,73 +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 { - CoreApi, - FlowDesignationEnum, - FlowsApi, - LDAPProviderRequest, - ProvidersApi, - UserServiceAccountResponse, -} from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-ldap") -export class TypeLDAPApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("LDAP details"); - - nextDataCallback = async (data: KeyUnknown): Promise