From 889adecad1bfb3a62027d85f135fd625504fd299 Mon Sep 17 00:00:00 2001 From: Ken Sternberg Date: Mon, 9 Oct 2023 14:56:29 -0700 Subject: [PATCH] web: better converter configuration, CSS repair, and forward-domain-proxy 1. Forward Domain Proxy. I wasn't sure if this method was appropriate for the wizard, but Jens says it is. I've added it. 2. In the process of doing so, I decided that the Provider.converter field was overly complexified; I tried too hard to reduce the number of functions I needed to define, but in the process outsourced some of the logic of converting the Wizard's dataset into a property typed request to the `commit` phase, which was inappropriate. All of the logic about a provider, aside from its display, should be here with the code that distinguishes between providers. This commit makes it so. 3. Small CSS fix: the fields inherited from the Proxy provider forms had some unexpected CSS which was causing a bit of a weird indent. That has been rectified. --- ...rd-authentication-method-choice.choices.ts | 121 +++++++++--------- ...k-application-wizard-commit-application.ts | 52 +++----- ...pplication-wizard-authentication-method.ts | 1 + .../proxy/AuthenticationByProxyPage.ts | 2 +- ...authentication-for-forward-domain-proxy.ts | 55 ++++++++ web/src/admin/applications/wizard/steps.ts | 6 +- 6 files changed, 136 insertions(+), 101 deletions(-) create mode 100644 web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-forward-domain-proxy.ts diff --git a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.choices.ts b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.choices.ts index f18b3dc43..dd50b2ae2 100644 --- a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.choices.ts +++ b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.choices.ts @@ -2,7 +2,8 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import type { ProviderModelEnum as ProviderModelEnumType, TypeCreate } from "@goauthentik/api"; -import { ProviderModelEnum } from "@goauthentik/api"; +import { ProviderModelEnum, ProxyMode, + } from "@goauthentik/api"; import type { LDAPProviderRequest, ModelRequest, @@ -17,10 +18,10 @@ import { OneOfProvider } from "../types"; type ProviderRenderer = () => TemplateResult; -type ProviderType = [string, string, string, ProviderRenderer, ProviderModelEnumType]; - type ModelConverter = (provider: OneOfProvider) => ModelRequest; +type ProviderType = [string, string, string, ProviderRenderer, ProviderModelEnumType, ModelConverter]; + export type LocalTypeCreate = TypeCreate & { formName: string; modelName: ProviderModelEnumType; @@ -35,6 +36,10 @@ const _providerModelsTable: ProviderType[] = [ msg("Modern applications, APIs and Single-page applications."), () => html``, ProviderModelEnum.Oauth2Oauth2provider, + (provider: OneOfProvider) => ({ + providerModel: ProviderModelEnum.Oauth2Oauth2provider, + ...(provider as OAuth2ProviderRequest), + }), ], [ "ldapprovider", @@ -42,20 +47,49 @@ const _providerModelsTable: ProviderType[] = [ msg("Provide an LDAP interface for applications and users to authenticate against."), () => html``, ProviderModelEnum.LdapLdapprovider, + (provider: OneOfProvider) => ({ + providerModel: ProviderModelEnum.LdapLdapprovider, + ...(provider as LDAPProviderRequest), + }), + ], [ "proxyprovider-proxy", msg("Transparent Reverse Proxy"), msg("For transparent reverse proxies with required authentication"), () => html``, - ProviderModelEnum.ProxyProxyprovider + ProviderModelEnum.ProxyProxyprovider, + (provider: OneOfProvider) => ({ + providerModel: ProviderModelEnum.ProxyProxyprovider, + ...(provider as ProxyProviderRequest), + mode: ProxyMode.Proxy, + }), + ], [ "proxyprovider-forwardsingle", - msg("Forward Single Proxy"), + msg("Forward Auth Single Application"), msg("For nginx's auth_request or traefix's forwardAuth"), () => html``, - ProviderModelEnum.ProxyProxyprovider + ProviderModelEnum.ProxyProxyprovider , + (provider: OneOfProvider) => ({ + providerModel: ProviderModelEnum.ProxyProxyprovider, + ...(provider as ProxyProviderRequest), + mode: ProxyMode.ForwardSingle, + }), + + ], + [ + "proxyprovider-forwarddomain", + msg("Forward Auth Domain Level"), + msg("For nginx's auth_request or traefix's forwardAuth per root domain"), + () => html``, + ProviderModelEnum.ProxyProxyprovider , + (provider: OneOfProvider) => ({ + providerModel: ProviderModelEnum.ProxyProxyprovider, + ...(provider as ProxyProviderRequest), + mode: ProxyMode.ForwardDomain, + }), ], [ @@ -63,93 +97,54 @@ const _providerModelsTable: ProviderType[] = [ msg("SAML Configuration"), msg("Configure SAML provider manually"), () => html``, - ProviderModelEnum.SamlSamlprovider + ProviderModelEnum.SamlSamlprovider, + (provider: OneOfProvider) => ({ + providerModel: ProviderModelEnum.SamlSamlprovider, + ...(provider as SAMLProviderRequest), + }), + ], [ "radiusprovider", msg("RADIUS Configuration"), msg("Configure RADIUS provider manually"), () => html``, - ProviderModelEnum.RadiusRadiusprovider + ProviderModelEnum.RadiusRadiusprovider, + (provider: OneOfProvider) => ({ + providerModel: ProviderModelEnum.RadiusRadiusprovider, + ...(provider as RadiusProviderRequest), + }), + ], [ "scimprovider", msg("SCIM Manual configuration"), msg("Configure SCIM provider manually"), () => html``, - ProviderModelEnum.ScimScimprovider - ], -]; - -const converters = new Map([ - [ - ProviderModelEnum.Oauth2Oauth2provider, - (provider: OneOfProvider) => ({ - providerModel: ProviderModelEnum.Oauth2Oauth2provider, - ...(provider as OAuth2ProviderRequest), - }), - ], - [ - ProviderModelEnum.LdapLdapprovider, - (provider: OneOfProvider) => ({ - providerModel: ProviderModelEnum.LdapLdapprovider, - ...(provider as LDAPProviderRequest), - }), - ], - [ - ProviderModelEnum.ProxyProxyprovider, - (provider: OneOfProvider) => ({ - providerModel: ProviderModelEnum.ProxyProxyprovider, - ...(provider as ProxyProviderRequest), - }), - ], - [ - ProviderModelEnum.SamlSamlprovider, - (provider: OneOfProvider) => ({ - providerModel: ProviderModelEnum.SamlSamlprovider, - ...(provider as SAMLProviderRequest), - }), - ], - [ ProviderModelEnum.ScimScimprovider, - (provider: OneOfProvider) => ({ + (provider: OneOfProvider) => ({ providerModel: ProviderModelEnum.ScimScimprovider, ...(provider as SCIMProviderRequest), }), - ], - [ - ProviderModelEnum.RadiusRadiusprovider, - (provider: OneOfProvider) => ({ - providerModel: ProviderModelEnum.RadiusRadiusprovider, - ...(provider as RadiusProviderRequest), - }), - ], -]); -// Contract enforcement -const getConverter = (modelName: ProviderModelEnumType): ModelConverter => { - const maybeConverter = converters.get(modelName); - if (!maybeConverter) { - throw new Error(`ModelName lookup failed in model converter definition: ${"modelName"}`); - } - return maybeConverter; -}; + ], +]; -function mapProviders([formName, name, description, _, modelName]: ProviderType): LocalTypeCreate { +function mapProviders([formName, name, description, _, modelName, converter]: ProviderType): LocalTypeCreate { return { formName, name, description, component: "", modelName, - converter: getConverter(modelName), + converter }; } export const providerModelsList = _providerModelsTable.map(mapProviders); export const providerRendererList = new Map( - _providerModelsTable.map(([modelName, _0, _1, renderer]) => [modelName, renderer]), + _providerModelsTable.map(([modelName, _0, _1, renderer]) => [modelName, renderer]) ); export default providerModelsList; diff --git a/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts b/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts index 906856344..9d8af10b7 100644 --- a/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts +++ b/web/src/admin/applications/wizard/commit/ak-application-wizard-commit-application.ts @@ -58,7 +58,7 @@ const runningState: State = { }; const errorState: State = { state: "error", - label: msg("There was an error in saving your application:"), + label: msg("Authentik was unable to save this application:"), icon: ["fa-times-circle", "pf-m-danger"], }; @@ -68,21 +68,6 @@ const successState: State = { icon: ["fa-check-circle", "pf-m-success"], }; -function extract(o: Record): string[] { - function inner(o: Record): string[] { - if (typeof o !== "object") { - return []; - } - if (Array.isArray(o)) { - return o; - } - return Object.keys(o) - .map((k) => inner(o[k])) - .flat(); - } - return inner(o); -} - @customElement("ak-application-wizard-commit-application") export class ApplicationWizardCommitApplication extends BasePanel { static get styles() { @@ -126,26 +111,10 @@ export class ApplicationWizardCommitApplication extends BasePanel { ); } - const provider = (() => { - if (this.wizard.providerModel === "proxyprovider-forwardsingle") { - return { - ...providerModel.converter(this.wizard.provider), - mode: ProxyMode.ForwardSingle, - }; - } - if (this.wizard.providerModel === "proxyprovider-proxy") { - return { - ...providerModel.converter(this.wizard.provider), - mode: ProxyMode.Proxy, - }; - } - return providerModel.converter(this.wizard.provider); - })(); - const request: TransactionApplicationRequest = { providerModel: providerModel.modelName as ProviderModelType, app: cleanApplication(this.wizard.app), - provider, + provider: providerModel.converter(this.wizard.provider) }; this.send(request); @@ -153,6 +122,21 @@ export class ApplicationWizardCommitApplication extends BasePanel { } } + decodeErrors(body: Record) { + const spaceify = (src: Record) => + Object.values(src).map((msg) => `\u00a0\u00a0\u00a0\u00a0${msg}`); + + let errs: string[] = []; + if (body["app"] !== undefined) { + errs = [...errs, msg("In the Application:"), ...spaceify(body["app"])]; + } + if (body["provider"] !== undefined) { + errs = [...errs, msg("In the Provider:"), ...spaceify(body["provider"])]; + } + console.log(body, errs); + return errs; + } + async send( data: TransactionApplicationRequest, ): Promise { @@ -170,7 +154,7 @@ export class ApplicationWizardCommitApplication extends BasePanel { }) .catch((resolution: any) => { resolution.response.json().then((body: Record) => { - this.errors = extract(body); + this.errors = this.decodeErrors(body); this.commitState = errorState; }); }); diff --git a/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts b/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts index f308bcbab..36d412315 100644 --- a/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts +++ b/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts @@ -6,6 +6,7 @@ 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 "./proxy/ak-application-wizard-authentication-for-forward-domain-proxy"; import "./radius/ak-application-wizard-authentication-by-radius"; import "./saml/ak-application-wizard-authentication-by-saml-configuration"; import "./scim/ak-application-wizard-authentication-by-scim"; diff --git a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts index c3ac7f44f..4b32fe2d7 100644 --- a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts +++ b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts @@ -127,7 +127,7 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {

- + ${this.renderProxyMode()} + ${msg( + "Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application." + )} +

+
+ ${msg("An example setup can look like this:")} +
    +
  • ${msg("authentik running on auth.example.com")}
  • +
  • ${msg("app1 running on app1.example.com")}
  • +
+ ${msg( + "In this case, you'd set the Authentication URL to auth.example.com and Cookie domain to example.com." + )} +
`; + } + + renderProxyMode() { + return html` + + + + `; + } +} + +export default AkForwardDomainProxyApplicationWizardPage; diff --git a/web/src/admin/applications/wizard/steps.ts b/web/src/admin/applications/wizard/steps.ts index a04cc0bee..451367bf8 100644 --- a/web/src/admin/applications/wizard/steps.ts +++ b/web/src/admin/applications/wizard/steps.ts @@ -30,7 +30,7 @@ class ApplicationStep implements ApplicationStepType { class ProviderMethodStep implements ApplicationStepType { id = "provider-method"; - label = "Authentication Method"; + label = "Provider Type"; disabled = false; valid = false; @@ -47,7 +47,7 @@ class ProviderMethodStep implements ApplicationStepType { class ProviderStepDetails implements ApplicationStepType { id = "provider-details"; - label = "Authentication Details"; + label = "Provider Configuration"; disabled = true; valid = false; get buttons() { @@ -61,7 +61,7 @@ class ProviderStepDetails implements ApplicationStepType { class SubmitApplicationStep implements ApplicationStepType { id = "submit"; - label = "Submit New Application"; + label = "Submit Application"; disabled = true; valid = false;