web: Tactical change. Put all the variants on the second page; it's
a longer list, but it's also easier to manage than all those required sub-options.
This commit is contained in:
parent
303278964e
commit
fe17f116ed
|
@ -0,0 +1,35 @@
|
|||
import { msg } from "@lit/localize";
|
||||
|
||||
import type { TypeCreate } from "@goauthentik/api";
|
||||
|
||||
type ProviderType = [string, string, string] | [string, string, string, ProviderType[]];
|
||||
|
||||
type ProviderOption = TypeCreate & {
|
||||
children?: TypeCreate[];
|
||||
};
|
||||
|
||||
// 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")]
|
||||
];
|
||||
|
||||
function mapProviders([modelName, name, description, children]: ProviderType): ProviderOption {
|
||||
return {
|
||||
modelName,
|
||||
name,
|
||||
description,
|
||||
component: "",
|
||||
...(children ? { children: children.map(mapProviders) } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export const providerTypesList = _providerTypesTable.map(mapProviders);
|
||||
|
||||
export default providerTypesList;
|
|
@ -1,4 +1,3 @@
|
|||
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";
|
||||
|
@ -9,73 +8,45 @@ 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";
|
||||
|
||||
// The provider description that comes from the server is fairly specific and not internationalized.
|
||||
// We provide alternative descriptions that use the phrase 'authentication method' instead, and make
|
||||
// it available to i18n.
|
||||
//
|
||||
// prettier-ignore
|
||||
const alternativeDescription = new Map<string, string>([
|
||||
["oauth2provider", msg("Modern applications, APIs and Single-page applications.")],
|
||||
["samlprovider", msg("XML-based SSO standard. Use this if your application only supports SAML.")],
|
||||
["proxyprovider", msg("Legacy applications which don't natively support SSO.")],
|
||||
["ldapprovider", msg("Provide an LDAP interface for applications and users to authenticate against.")]
|
||||
]);
|
||||
import providerTypesList from "./ak-application-wizard-authentication-method-choice.choices";
|
||||
|
||||
@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);
|
||||
// If the provider doesn't supply a model to which to send our initialization, the user will
|
||||
// have to use the older provider path.
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
|
||||
this.providerTypes = types.filter(({ modelName }) => modelName.trim() !== "");
|
||||
});
|
||||
}
|
||||
|
||||
handleChoice(ev: InputEvent) {
|
||||
const target = ev.target as HTMLInputElement;
|
||||
|
||||
this.dispatchWizardUpdate({ providerType: target.value });
|
||||
}
|
||||
|
||||
renderProvider(type: TypeCreate) {
|
||||
const description = alternativeDescription.has(type.modelName)
|
||||
? alternativeDescription.get(type.modelName)
|
||||
: type.description;
|
||||
|
||||
const label = type.name.replace(/\s+Provider/, "");
|
||||
|
||||
return html`<div class="pf-c-radio">
|
||||
<input
|
||||
class="pf-c-radio__input"
|
||||
type="radio"
|
||||
name="type"
|
||||
id=${type.component}
|
||||
id="provider-${type.modelName}"
|
||||
value=${type.modelName}
|
||||
@change=${this.handleChoice}
|
||||
/>
|
||||
<label class="pf-c-radio__label" for=${type.component}>${label}</label>
|
||||
<span class="pf-c-radio__description">${description}</span>
|
||||
<label class="pf-c-radio__label" for="provider-${type.modelName}">${type.name}</label>
|
||||
<span class="pf-c-radio__description">${type.description}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.providerTypes.length > 0
|
||||
return providerTypesList.length > 0
|
||||
? html`<form class="pf-c-form pf-m-horizontal">
|
||||
${map(this.providerTypes, this.renderProvider)}
|
||||
${map(providerTypesList, this.renderProvider)}
|
||||
</form>`
|
||||
: html`<ak-empty-state loading header=${msg("Loading")}></ak-empty-state>`;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement, state } from "@lit/reactive-element/decorators.js";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
import "@goauthentik/components/ak-toggle-group";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { state } from "@lit/reactive-element/decorators.js";
|
||||
import { TemplateResult, html, nothing } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
FlowsInstancesListDesignationEnum,
|
||||
|
@ -24,7 +23,8 @@ import {
|
|||
|
||||
import ApplicationWizardPageBase from "../ApplicationWizardPageBase";
|
||||
|
||||
@customElement("ak-application-wizard-authentication-by-proxy")
|
||||
type MaybeTemplateResult = TemplateResult | typeof nothing;
|
||||
|
||||
export class AkTypeProxyApplicationWizardPage extends ApplicationWizardPageBase {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -72,6 +72,14 @@ export class AkTypeProxyApplicationWizardPage extends ApplicationWizardPageBase
|
|||
return this.wizard.provider as ProxyProvider;
|
||||
}
|
||||
|
||||
renderModeDescription(): MaybeTemplateResult {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
renderProxyMode() {
|
||||
return html`<h2>This space intentionally left blank</h2>`;
|
||||
}
|
||||
|
||||
renderHttpBasic(): TemplateResult {
|
||||
return html`<ak-text-input
|
||||
name="basicAuthUserAttribute"
|
||||
|
@ -94,120 +102,9 @@ export class AkTypeProxyApplicationWizardPage extends ApplicationWizardPageBase
|
|||
</ak-text-input>`;
|
||||
}
|
||||
|
||||
renderModeSelector(): TemplateResult {
|
||||
const setMode = (ev: CustomEvent<{ value: ProxyMode }>) => {
|
||||
this.mode = ev.detail.value;
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
return html`
|
||||
<ak-toggle-group value=${this.mode} @ak-toggle=${setMode}>
|
||||
<option value=${ProxyMode.Proxy}>${msg("Proxy")}</option>
|
||||
<option value=${ProxyMode.ForwardSingle}>${msg("Forward auth (single application)")}</option>
|
||||
<option value=${ProxyMode.ForwardDomain}>${msg("Forward auth (domain level)")}</option>
|
||||
</ak-toggle-group>
|
||||
`;
|
||||
}
|
||||
|
||||
renderProxyModeProxy() {
|
||||
return html`<p class="pf-u-mb-xl">
|
||||
${msg(
|
||||
"This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well.",
|
||||
)}
|
||||
</p>
|
||||
<ak-text-input
|
||||
name="externalHost"
|
||||
value=${ifDefined(this.instance?.externalHost)}
|
||||
required
|
||||
label=${msg("External host")}
|
||||
help=${msg(
|
||||
"The external URL you'll access the application at. Include any non-standard port.",
|
||||
)}
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="internalHost"
|
||||
value=${ifDefined(this.instance?.internalHost)}
|
||||
required
|
||||
label=${msg("Internal host")}
|
||||
help=${msg("Upstream host that the requests are forwarded to.")}
|
||||
></ak-text-input>
|
||||
<ak-switch-input
|
||||
name="internalHostSslValidation"
|
||||
?checked=${first(this.instance?.internalHostSslValidation, true)}
|
||||
label=${msg("Internal host SSL Validation")}
|
||||
help=${msg("Validate SSL Certificates of upstream servers.")}
|
||||
>
|
||||
</ak-switch-input>`;
|
||||
}
|
||||
|
||||
renderProxyModeForwardSingle() {
|
||||
return html`<p class="pf-u-mb-xl">
|
||||
${msg(
|
||||
"Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).",
|
||||
)}
|
||||
</p>
|
||||
<ak-text-input
|
||||
name="externalHost"
|
||||
value=${ifDefined(this.instance?.externalHost)}
|
||||
required
|
||||
label=${msg("External host")}
|
||||
help=${msg(
|
||||
"The external URL you'll access the application at. Include any non-standard port.",
|
||||
)}
|
||||
></ak-text-input>`;
|
||||
}
|
||||
|
||||
renderProxyModeForwardDomain() {
|
||||
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>
|
||||
<ak-text-input
|
||||
name="externalHost"
|
||||
value=${first(this.instance?.externalHost, window.location.origin)}
|
||||
required
|
||||
label=${msg("Authentication URL")}
|
||||
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"
|
||||
value=${ifDefined(this.instance?.cookieDomain)}
|
||||
required
|
||||
label=${msg("Cookie domain")}
|
||||
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>`;
|
||||
}
|
||||
|
||||
renderSettings() {
|
||||
switch (this.mode) {
|
||||
case ProxyMode.Proxy:
|
||||
return this.renderProxyModeProxy();
|
||||
case ProxyMode.ForwardSingle:
|
||||
return this.renderProxyModeForwardSingle();
|
||||
case ProxyMode.ForwardDomain:
|
||||
return this.renderProxyModeForwardDomain();
|
||||
case ProxyMode.UnknownDefaultOpenApi:
|
||||
return html`<p>${msg("Unknown proxy mode")}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||
${this.renderModeDescription()}
|
||||
<ak-text-input
|
||||
name="name"
|
||||
value=${ifDefined(this.instance?.name)}
|
||||
|
@ -243,10 +140,8 @@ export class AkTypeProxyApplicationWizardPage extends ApplicationWizardPageBase
|
|||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<div class="pf-c-card pf-m-selectable pf-m-selected">
|
||||
<div class="pf-c-card__body">${this.renderModeSelector()}</div>
|
||||
<div class="pf-c-card__footer">${this.renderSettings()}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">${this.renderProxyMode()}</div>
|
||||
|
||||
<ak-text-input
|
||||
name="accessTokenValidity"
|
||||
value=${first(this.instance?.accessTokenValidity, "hours=24")}
|
|
@ -0,0 +1,49 @@
|
|||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
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-reverse-proxy")
|
||||
export class AkReverseProxyApplicationWizardPage extends AkTypeProxyApplicationWizardPage {
|
||||
renderModeDescription() {
|
||||
return html`<p class="pf-u-mb-xl">
|
||||
${msg(
|
||||
"This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well.",
|
||||
)}
|
||||
</p>`;
|
||||
}
|
||||
|
||||
renderProxyMode() {
|
||||
return html` <ak-text-input
|
||||
name="externalHost"
|
||||
value=${ifDefined(this.instance?.externalHost)}
|
||||
required
|
||||
label=${msg("External host")}
|
||||
help=${msg(
|
||||
"The external URL you'll access the application at. Include any non-standard port.",
|
||||
)}
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="internalHost"
|
||||
value=${ifDefined(this.instance?.internalHost)}
|
||||
required
|
||||
label=${msg("Internal host")}
|
||||
help=${msg("Upstream host that the requests are forwarded to.")}
|
||||
></ak-text-input>
|
||||
<ak-switch-input
|
||||
name="internalHostSslValidation"
|
||||
?checked=${first(this.instance?.internalHostSslValidation, true)}
|
||||
label=${msg("Internal host SSL Validation")}
|
||||
help=${msg("Validate SSL Certificates of upstream servers.")}
|
||||
>
|
||||
</ak-switch-input>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default AkReverseProxyApplicationWizardPage;
|
|
@ -0,0 +1,36 @@
|
|||
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-single-forward-proxy")
|
||||
export class AkForwardSingleProxyApplicationWizardPage extends AkTypeProxyApplicationWizardPage {
|
||||
renderModeDescription() {
|
||||
return html`<p class="pf-u-mb-xl">
|
||||
${msg(
|
||||
html`Use this provider with nginx's <code>auth_request</code> or traefik's
|
||||
<code>forwardAuth</code>. Each application/domain needs its own provider.
|
||||
Additionally, on each domain, <code>/outpost.goauthentik.io</code> must be
|
||||
routed to the outpost (when using a managed outpost, this is done for you).`,
|
||||
)}
|
||||
</p>`;
|
||||
}
|
||||
|
||||
renderProxyMode() {
|
||||
return html`<ak-text-input
|
||||
name="externalHost"
|
||||
value=${ifDefined(this.instance?.externalHost)}
|
||||
required
|
||||
label=${msg("External host")}
|
||||
help=${msg(
|
||||
"The external URL you'll access the application at. Include any non-standard port.",
|
||||
)}
|
||||
></ak-text-input>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default AkForwardSingleProxyApplicationWizardPage;
|
|
@ -7,8 +7,9 @@ import AkApplicationWizardApplicationDetails from "../ak-application-wizard-appl
|
|||
import "../ak-application-wizard-authentication-method-choice";
|
||||
import "../ak-application-wizard-context";
|
||||
import "../ldap/ak-application-wizard-authentication-by-ldap";
|
||||
import "../proxy/ak-application-wizard-authentication-by-proxy";
|
||||
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 "./ak-application-context-display-for-test";
|
||||
import {
|
||||
dummyAuthenticationFlowsSearch,
|
||||
|
@ -103,7 +104,7 @@ const container = (testItem: TemplateResult) => {
|
|||
</div>`;
|
||||
};
|
||||
|
||||
export const PageOne = () => {
|
||||
export const DescribeApplication = () => {
|
||||
return container(
|
||||
html`<ak-application-wizard-context>
|
||||
<ak-application-wizard-application-details></ak-application-wizard-application-details>
|
||||
|
@ -113,7 +114,7 @@ export const PageOne = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const PageTwo = () => {
|
||||
export const ChooseAuthMethod = () => {
|
||||
return container(
|
||||
html`<ak-application-wizard-context>
|
||||
<ak-application-wizard-authentication-method-choice></ak-application-wizard-authentication-method-choice>
|
||||
|
@ -123,7 +124,7 @@ export const PageTwo = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const PageThreeLdap = () => {
|
||||
export const ConfigureLdap = () => {
|
||||
return container(
|
||||
html`<ak-application-wizard-context>
|
||||
<ak-application-wizard-authentication-by-ldap></ak-application-wizard-authentication-by-ldap>
|
||||
|
@ -133,7 +134,7 @@ export const PageThreeLdap = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const PageThreeOauth2 = () => {
|
||||
export const ConfigureOauth2 = () => {
|
||||
return container(
|
||||
html`<ak-application-wizard-context>
|
||||
<ak-application-wizard-authentication-by-oauth></ak-application-wizard-authentication-by-oauth>
|
||||
|
@ -143,10 +144,20 @@ export const PageThreeOauth2 = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const PageThreeProxy = () => {
|
||||
export const ConfigureReverseProxy = () => {
|
||||
return container(
|
||||
html`<ak-application-wizard-context>
|
||||
<ak-application-wizard-authentication-by-proxy></ak-application-wizard-authentication-by-proxy>
|
||||
<ak-application-wizard-authentication-for-reverse-proxy></ak-application-wizard-authentication-for-reverse-proxy>
|
||||
<hr />
|
||||
<ak-application-context-display-for-test></ak-application-context-display-for-test>
|
||||
</ak-application-wizard-context>`,
|
||||
);
|
||||
};
|
||||
|
||||
export const ConfigureSingleForwardProxy = () => {
|
||||
return container(
|
||||
html`<ak-application-wizard-context>
|
||||
<ak-application-wizard-authentication-for-single-forward-proxy></ak-application-wizard-authentication-for-single-forward-proxy>
|
||||
<hr />
|
||||
<ak-application-context-display-for-test></ak-application-context-display-for-test>
|
||||
</ak-application-wizard-context>`,
|
||||
|
|
Reference in New Issue