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.
This commit is contained in:
parent
478258d88f
commit
889adecad1
|
@ -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`<ak-application-wizard-authentication-by-oauth></ak-application-wizard-authentication-by-oauth>`,
|
||||
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`<ak-application-wizard-authentication-by-ldap></ak-application-wizard-authentication-by-ldap>`,
|
||||
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`<ak-application-wizard-authentication-for-reverse-proxy></ak-application-wizard-authentication-for-reverse-proxy>`,
|
||||
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`<ak-application-wizard-authentication-for-single-forward-proxy></ak-application-wizard-authentication-for-single-forward-proxy>`,
|
||||
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`<ak-application-wizard-authentication-for-forward-proxy-domain></ak-application-wizard-authentication-for-forward-proxy-domain>`,
|
||||
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`<ak-application-wizard-authentication-by-saml-configuration></ak-application-wizard-authentication-by-saml-configuration>`,
|
||||
ProviderModelEnum.SamlSamlprovider
|
||||
ProviderModelEnum.SamlSamlprovider,
|
||||
(provider: OneOfProvider) => ({
|
||||
providerModel: ProviderModelEnum.SamlSamlprovider,
|
||||
...(provider as SAMLProviderRequest),
|
||||
}),
|
||||
|
||||
],
|
||||
[
|
||||
"radiusprovider",
|
||||
msg("RADIUS Configuration"),
|
||||
msg("Configure RADIUS provider manually"),
|
||||
() => html`<ak-application-wizard-authentication-by-radius></ak-application-wizard-authentication-by-radius>`,
|
||||
ProviderModelEnum.RadiusRadiusprovider
|
||||
ProviderModelEnum.RadiusRadiusprovider,
|
||||
(provider: OneOfProvider) => ({
|
||||
providerModel: ProviderModelEnum.RadiusRadiusprovider,
|
||||
...(provider as RadiusProviderRequest),
|
||||
}),
|
||||
|
||||
],
|
||||
[
|
||||
"scimprovider",
|
||||
msg("SCIM Manual configuration"),
|
||||
msg("Configure SCIM provider manually"),
|
||||
() => html`<ak-application-wizard-authentication-by-scim></ak-application-wizard-authentication-by-scim>`,
|
||||
ProviderModelEnum.ScimScimprovider
|
||||
],
|
||||
];
|
||||
|
||||
const converters = new Map<ProviderModelEnumType, ModelConverter>([
|
||||
[
|
||||
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<string, ProviderRenderer>(
|
||||
_providerModelsTable.map(([modelName, _0, _1, renderer]) => [modelName, renderer]),
|
||||
_providerModelsTable.map(([modelName, _0, _1, renderer]) => [modelName, renderer])
|
||||
);
|
||||
|
||||
export default providerModelsList;
|
||||
|
|
|
@ -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, any>): string[] {
|
||||
function inner(o: Record<string, any>): 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<string, any>) {
|
||||
const spaceify = (src: Record<string, string>) =>
|
||||
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<TransactionApplicationResponse | void> {
|
||||
|
@ -170,7 +154,7 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
|||
})
|
||||
.catch((resolution: any) => {
|
||||
resolution.response.json().then((body: Record<string, any>) => {
|
||||
this.errors = extract(body);
|
||||
this.errors = this.decodeErrors(body);
|
||||
this.commitState = errorState;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -127,7 +127,7 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
|||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<div class="pf-c-card__footer">${this.renderProxyMode()}</div>
|
||||
${this.renderProxyMode()}
|
||||
|
||||
<ak-text-input
|
||||
name="accessTokenValidity"
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import "@goauthentik/components/ak-text-input";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement } from "@lit/reactive-element/decorators.js";
|
||||
import { html } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage";
|
||||
|
||||
@customElement("ak-application-wizard-authentication-for-forward-proxy-domain")
|
||||
export class AkForwardDomainProxyApplicationWizardPage extends AkTypeProxyApplicationWizardPage {
|
||||
renderModeDescription() {
|
||||
return html`<p class="pf-u-mb-xl">
|
||||
${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."
|
||||
)}
|
||||
</p>
|
||||
<div class="pf-u-mb-xl">
|
||||
${msg("An example setup can look like this:")}
|
||||
<ul class="pf-c-list">
|
||||
<li>${msg("authentik running on auth.example.com")}</li>
|
||||
<li>${msg("app1 running on app1.example.com")}</li>
|
||||
</ul>
|
||||
${msg(
|
||||
"In this case, you'd set the Authentication URL to auth.example.com and Cookie domain to example.com."
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderProxyMode() {
|
||||
return html`
|
||||
<ak-text-input
|
||||
name="externalHost"
|
||||
label=${msg("External host")}
|
||||
value=${ifDefined(this.instance?.externalHost)}
|
||||
required
|
||||
help=${msg(
|
||||
"The external URL you'll authenticate at. The authentik core server should be reachable under this URL."
|
||||
)}
|
||||
>
|
||||
</ak-text-input>
|
||||
<ak-text-input
|
||||
name="cookieDomain"
|
||||
label=${msg("Cookie domain")}
|
||||
value="${ifDefined(this.instance?.cookieDomain)}"
|
||||
required
|
||||
help=${msg(
|
||||
"Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'."
|
||||
)}
|
||||
></ak-text-input>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default AkForwardDomainProxyApplicationWizardPage;
|
|
@ -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;
|
||||
|
||||
|
|
Reference in a new issue