
* web: Add InvalidationFlow to Radius Provider dialogues
## What
- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
- Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`
## Note
Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.
* This (temporary) change is needed to prevent the unit tests from failing.
\# What
\# Why
\# How
\# Designs
\# Test Steps
\# Other Notes
* Revert "This (temporary) change is needed to prevent the unit tests from failing."
This reverts commit dddde09be5
.
* web/components: Remove all special cases of slug handling, replace with a "smart slug" component
This commit removes all special handling for the `slug` attribute in our text. A variant of the text
input control that can handle formatting-as-slugs has replaced all the slugificiation code; simply
drop it onto a page and tell it the (must be unique) selector from which to get the data to be
slugified. It only looks up one tier of the DOM so be careful that both the text input and its slug
accessory occupy the same DOM context.
## Details
### The Component
Now that we know a (lot) more about Lit, this component has been slightly updated to meet our
current standards.
- web/src/components/ak-slug-input.ts
Changes made:
- The "listen for the source object" has been moved to the `firstUpdated`, so that it no longer has
to wait for the end of a render.
- The `dirtyFlag` handler now uses the `@input` syntax.
- Updated the slug formatter to permit trailing dashes.
- Uses the `@bound` decorator, eliminating the need to do binding in the constructor (and so
eliminating the constructor completely).
### Component uses:
The following components were revised to use `ak-slug-input` instead of a plain text input with the
slug-handling added by our forms manager.
- applications/ApplicationForm.ts
- flows/FlowForm.ts
- sources/kerberos/KerberosSourceForm.ts
- sources/ldap/LDAPSourceForm.ts
- sources/oauth/OAuthSourceForm.ts
- sources/plex/PlexSourceForm.ts
- sources/saml/SAMLSourceForm.ts
- sources/scim/SCIMSourceForm.ts
### Remove the redundant special slug handling code
- web/src/elements/forms/Form.ts
- web/src/elements/forms/HorizontalFormElement.ts
### A special case among special cases
- web/src/admin/stages/invitation/InvitationForm.ts
This form is our one case where we have a slug input field with no corresponding text source. Adding
a simple event handler to validate the value whenever it changed and write back a "clean" slug was
the most straightforward solution. I added a help line; it seemed "surprising" to ask someone for a
name and not follow the same rules as "names" everywhere else in our UI without explanation.
* After writing the commit message, I realized some of the comments I made MUST be added to the component.
* The `source` attribute needed its own comment to indicate that a `query()` compatible selector is expected.
* Added public/private/protected/# indicators to all fields. Trying to balance between getting it 'right' and leaving an opening for harmonizing style-sharing and state-sharing between (text / textarea), slug, password and (visible / hidden / secret).
* Removed the ids as requested; the default "look for this" matches the original behavior without requiring it be hard-coded and unchangable.
561 lines
26 KiB
TypeScript
561 lines
26 KiB
TypeScript
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
|
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
|
|
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
|
|
import { policyEngineModes } from "@goauthentik/admin/policies/PolicyEngineModes";
|
|
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
|
import {
|
|
GroupMatchingModeToLabel,
|
|
UserMatchingModeToLabel,
|
|
} from "@goauthentik/admin/sources/oauth/utils";
|
|
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
|
import "@goauthentik/components/ak-radio-input";
|
|
import "@goauthentik/components/ak-secret-textarea-input.js";
|
|
import "@goauthentik/components/ak-slug-input.js";
|
|
import "@goauthentik/elements/CodeMirror";
|
|
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
|
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
|
import "@goauthentik/elements/forms/FormGroup";
|
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
|
import "@goauthentik/elements/forms/Radio";
|
|
import "@goauthentik/elements/forms/SearchSelect";
|
|
|
|
import { msg } from "@lit/localize";
|
|
import { PropertyValues, TemplateResult, html } from "lit";
|
|
import { customElement, property, state } from "lit/decorators.js";
|
|
import { ifDefined } from "lit/directives/if-defined.js";
|
|
|
|
import {
|
|
AuthorizationCodeAuthMethodEnum,
|
|
FlowsInstancesListDesignationEnum,
|
|
GroupMatchingModeEnum,
|
|
OAuthSource,
|
|
OAuthSourceRequest,
|
|
ProviderTypeEnum,
|
|
SourceType,
|
|
SourcesApi,
|
|
UserMatchingModeEnum,
|
|
} from "@goauthentik/api";
|
|
|
|
import { propertyMappingsProvider, propertyMappingsSelector } from "./OAuthSourceFormHelpers.js";
|
|
|
|
const authorizationCodeAuthMethodOptions = [
|
|
{
|
|
label: msg("HTTP Basic Auth"),
|
|
value: AuthorizationCodeAuthMethodEnum.BasicAuth,
|
|
default: true,
|
|
},
|
|
{
|
|
label: msg("Include the client ID and secret as request parameters"),
|
|
value: AuthorizationCodeAuthMethodEnum.PostBody,
|
|
},
|
|
];
|
|
|
|
@customElement("ak-source-oauth-form")
|
|
export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuthSource>) {
|
|
async loadInstance(pk: string): Promise<OAuthSource> {
|
|
const source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthRetrieve({
|
|
slug: pk,
|
|
});
|
|
this.providerType = source.type;
|
|
this.clearIcon = false;
|
|
return source;
|
|
}
|
|
|
|
_modelName?: string;
|
|
|
|
@property()
|
|
modelName?: string;
|
|
|
|
@property({ attribute: false })
|
|
providerType: SourceType | null = null;
|
|
|
|
@state()
|
|
clearIcon = false;
|
|
|
|
async send(data: OAuthSource): Promise<OAuthSource> {
|
|
data.providerType = (this.providerType?.name || "") as ProviderTypeEnum;
|
|
let source: OAuthSource;
|
|
if (this.instance) {
|
|
source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthPartialUpdate({
|
|
slug: this.instance.slug,
|
|
patchedOAuthSourceRequest: data,
|
|
});
|
|
} else {
|
|
source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthCreate({
|
|
oAuthSourceRequest: data as unknown as OAuthSourceRequest,
|
|
});
|
|
}
|
|
const c = await config();
|
|
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
|
|
const icon = this.getFormFiles().icon;
|
|
if (icon || this.clearIcon) {
|
|
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({
|
|
slug: source.slug,
|
|
file: icon,
|
|
clear: this.clearIcon,
|
|
});
|
|
}
|
|
} else {
|
|
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconUrlCreate({
|
|
slug: source.slug,
|
|
filePathRequest: {
|
|
url: data.icon || "",
|
|
},
|
|
});
|
|
}
|
|
return source;
|
|
}
|
|
|
|
fetchProviderType(v: string | undefined) {
|
|
new SourcesApi(DEFAULT_CONFIG)
|
|
.sourcesOauthSourceTypesList({
|
|
name: v?.replace("oauthsource", ""),
|
|
})
|
|
.then((type) => {
|
|
this.providerType = type[0];
|
|
});
|
|
}
|
|
|
|
willUpdate(changedProperties: PropertyValues<this>) {
|
|
if (changedProperties.has("modelName")) {
|
|
this.fetchProviderType(this.modelName);
|
|
}
|
|
}
|
|
|
|
renderUrlOptions(): TemplateResult {
|
|
if (!this.providerType?.urlsCustomizable) {
|
|
return html``;
|
|
}
|
|
return html` <ak-form-group expanded>
|
|
<span slot="header"> ${msg("URL settings")} </span>
|
|
<div slot="body" class="pf-c-form">
|
|
<ak-form-element-horizontal
|
|
label=${msg("Authorization URL")}
|
|
name="authorizationUrl"
|
|
>
|
|
<input
|
|
type="text"
|
|
value="${this.instance?.authorizationUrl ??
|
|
this.providerType.authorizationUrl ??
|
|
""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("URL the user is redirect to to consent the authorization.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal label=${msg("Access token URL")} name="accessTokenUrl">
|
|
<input
|
|
type="url"
|
|
value="${this.instance?.accessTokenUrl ??
|
|
this.providerType.accessTokenUrl ??
|
|
""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("URL used by authentik to retrieve tokens.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal label=${msg("Profile URL")} name="profileUrl">
|
|
<input
|
|
type="url"
|
|
value="${this.instance?.profileUrl ?? this.providerType.profileUrl ?? ""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("URL used by authentik to get user information.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
${this.providerType.requestTokenUrl
|
|
? html`<ak-form-element-horizontal
|
|
label=${msg("Request token URL")}
|
|
name="requestTokenUrl"
|
|
>
|
|
<input
|
|
type="url"
|
|
value="${this.instance?.requestTokenUrl ?? ""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
/>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
"URL used to request the initial token. This URL is only required for OAuth 1.",
|
|
)}
|
|
</p>
|
|
</ak-form-element-horizontal> `
|
|
: html``}
|
|
${this.providerType.name === ProviderTypeEnum.Openidconnect ||
|
|
this.providerType.oidcWellKnownUrl !== ""
|
|
? html`<ak-form-element-horizontal
|
|
label=${msg("OIDC Well-known URL")}
|
|
name="oidcWellKnownUrl"
|
|
>
|
|
<input
|
|
type="url"
|
|
value="${this.instance?.oidcWellKnownUrl ??
|
|
this.providerType.oidcWellKnownUrl ??
|
|
""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
"OIDC well-known configuration URL. Can be used to automatically configure the URLs above.",
|
|
)}
|
|
</p>
|
|
</ak-form-element-horizontal>`
|
|
: html``}
|
|
${this.providerType.name === ProviderTypeEnum.Openidconnect ||
|
|
this.providerType.oidcJwksUrl !== ""
|
|
? html`<ak-form-element-horizontal
|
|
label=${msg("OIDC JWKS URL")}
|
|
name="oidcJwksUrl"
|
|
>
|
|
<input
|
|
type="url"
|
|
value="${this.instance?.oidcJwksUrl ??
|
|
this.providerType.oidcJwksUrl ??
|
|
""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
"JSON Web Key URL. Keys from the URL will be used to validate JWTs from this source.",
|
|
)}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal label=${msg("OIDC JWKS")} name="oidcJwks">
|
|
<ak-codemirror
|
|
mode=${CodeMirrorMode.JavaScript}
|
|
value="${JSON.stringify(this.instance?.oidcJwks ?? {})}"
|
|
>
|
|
</ak-codemirror>
|
|
<p class="pf-c-form__helper-text">${msg("Raw JWKS data.")}</p>
|
|
</ak-form-element-horizontal>`
|
|
: html``}
|
|
${this.providerType.name === ProviderTypeEnum.Openidconnect
|
|
? html`<ak-radio-input
|
|
label=${msg("Authorization code authentication method")}
|
|
name="authorizationCodeAuthMethod"
|
|
required
|
|
.options=${authorizationCodeAuthMethodOptions}
|
|
.value=${this.instance?.authorizationCodeAuthMethod}
|
|
help=${msg(
|
|
"How to perform authentication during an authorization_code token request flow",
|
|
)}
|
|
>
|
|
</ak-radio-input>`
|
|
: html``}
|
|
</div>
|
|
</ak-form-group>`;
|
|
}
|
|
|
|
renderForm(): TemplateResult {
|
|
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
|
<input
|
|
type="text"
|
|
value="${ifDefined(this.instance?.name)}"
|
|
class="pf-c-form-control"
|
|
required
|
|
/>
|
|
</ak-form-element-horizontal>
|
|
<ak-slug-input
|
|
name="slug"
|
|
value=${ifDefined(this.instance?.slug)}
|
|
label=${msg("Slug")}
|
|
required
|
|
input-hint="code"
|
|
></ak-slug-input>
|
|
<ak-form-element-horizontal name="enabled">
|
|
<label class="pf-c-switch">
|
|
<input
|
|
class="pf-c-switch__input"
|
|
type="checkbox"
|
|
?checked=${this.instance?.enabled ?? true}
|
|
/>
|
|
<span class="pf-c-switch__toggle">
|
|
<span class="pf-c-switch__toggle-icon">
|
|
<i class="fas fa-check" aria-hidden="true"></i>
|
|
</span>
|
|
</span>
|
|
<span class="pf-c-switch__label">${msg("Enabled")}</span>
|
|
</label>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal
|
|
label=${msg("User matching mode")}
|
|
required
|
|
name="userMatchingMode"
|
|
>
|
|
<select class="pf-c-form-control">
|
|
<option
|
|
value=${UserMatchingModeEnum.Identifier}
|
|
?selected=${this.instance?.userMatchingMode ===
|
|
UserMatchingModeEnum.Identifier}
|
|
>
|
|
${UserMatchingModeToLabel(UserMatchingModeEnum.Identifier)}
|
|
</option>
|
|
<option
|
|
value=${UserMatchingModeEnum.EmailLink}
|
|
?selected=${this.instance?.userMatchingMode ===
|
|
UserMatchingModeEnum.EmailLink}
|
|
>
|
|
${UserMatchingModeToLabel(UserMatchingModeEnum.EmailLink)}
|
|
</option>
|
|
<option
|
|
value=${UserMatchingModeEnum.EmailDeny}
|
|
?selected=${this.instance?.userMatchingMode ===
|
|
UserMatchingModeEnum.EmailDeny}
|
|
>
|
|
${UserMatchingModeToLabel(UserMatchingModeEnum.EmailDeny)}
|
|
</option>
|
|
<option
|
|
value=${UserMatchingModeEnum.UsernameLink}
|
|
?selected=${this.instance?.userMatchingMode ===
|
|
UserMatchingModeEnum.UsernameLink}
|
|
>
|
|
${UserMatchingModeToLabel(UserMatchingModeEnum.UsernameLink)}
|
|
</option>
|
|
<option
|
|
value=${UserMatchingModeEnum.UsernameDeny}
|
|
?selected=${this.instance?.userMatchingMode ===
|
|
UserMatchingModeEnum.UsernameDeny}
|
|
>
|
|
${UserMatchingModeToLabel(UserMatchingModeEnum.UsernameDeny)}
|
|
</option>
|
|
</select>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal
|
|
label=${msg("Group matching mode")}
|
|
required
|
|
name="groupMatchingMode"
|
|
>
|
|
<select class="pf-c-form-control">
|
|
<option
|
|
value=${GroupMatchingModeEnum.Identifier}
|
|
?selected=${this.instance?.groupMatchingMode ===
|
|
GroupMatchingModeEnum.Identifier}
|
|
>
|
|
${UserMatchingModeToLabel(UserMatchingModeEnum.Identifier)}
|
|
</option>
|
|
<option
|
|
value=${GroupMatchingModeEnum.NameLink}
|
|
?selected=${this.instance?.groupMatchingMode ===
|
|
GroupMatchingModeEnum.NameLink}
|
|
>
|
|
${GroupMatchingModeToLabel(GroupMatchingModeEnum.NameLink)}
|
|
</option>
|
|
<option
|
|
value=${GroupMatchingModeEnum.NameDeny}
|
|
?selected=${this.instance?.groupMatchingMode ===
|
|
GroupMatchingModeEnum.NameDeny}
|
|
>
|
|
${GroupMatchingModeToLabel(GroupMatchingModeEnum.NameDeny)}
|
|
</option>
|
|
</select>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal label=${msg("User path")} name="userPathTemplate">
|
|
<input
|
|
type="text"
|
|
value="${this.instance?.userPathTemplate ?? "goauthentik.io/sources/%(slug)s"}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">${placeholderHelperText}</p>
|
|
</ak-form-element-horizontal>
|
|
${this.can(CapabilitiesEnum.CanSaveMedia)
|
|
? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
|
<input type="file" value="" class="pf-c-form-control" />
|
|
${this.instance?.icon
|
|
? html`
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("Currently set to:")} ${this.instance?.icon}
|
|
</p>
|
|
`
|
|
: html``}
|
|
</ak-form-element-horizontal>
|
|
${this.instance?.icon
|
|
? html`
|
|
<ak-form-element-horizontal>
|
|
<label class="pf-c-switch">
|
|
<input
|
|
class="pf-c-switch__input"
|
|
type="checkbox"
|
|
@change=${(ev: Event) => {
|
|
const target = ev.target as HTMLInputElement;
|
|
this.clearIcon = target.checked;
|
|
}}
|
|
/>
|
|
<span class="pf-c-switch__toggle">
|
|
<span class="pf-c-switch__toggle-icon">
|
|
<i class="fas fa-check" aria-hidden="true"></i>
|
|
</span>
|
|
</span>
|
|
<span class="pf-c-switch__label">
|
|
${msg("Clear icon")}
|
|
</span>
|
|
</label>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("Delete currently set icon.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
`
|
|
: html``}`
|
|
: html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
|
<input
|
|
type="text"
|
|
value="${this.instance?.icon ?? ""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">${iconHelperText}</p>
|
|
</ak-form-element-horizontal>`}
|
|
|
|
<ak-form-group expanded>
|
|
<span slot="header"> ${msg("Protocol settings")} </span>
|
|
<div slot="body" class="pf-c-form">
|
|
<ak-form-element-horizontal
|
|
label=${msg("Consumer key")}
|
|
required
|
|
name="consumerKey"
|
|
>
|
|
<input
|
|
type="text"
|
|
value="${ifDefined(this.instance?.consumerKey)}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
required
|
|
/>
|
|
<p class="pf-c-form__helper-text">${msg("Also known as Client ID.")}</p>
|
|
</ak-form-element-horizontal>
|
|
<ak-secret-textarea-input
|
|
label=${msg("Consumer secret")}
|
|
name="consumerSecret"
|
|
input-hint="code"
|
|
help=${msg("Also known as Client Secret.")}
|
|
required
|
|
?revealed=${this.instance === undefined}
|
|
></ak-secret-textarea-input>
|
|
<ak-form-element-horizontal label=${msg("Scopes")} name="additionalScopes">
|
|
<input
|
|
type="text"
|
|
value="${this.instance?.additionalScopes ?? ""}"
|
|
class="pf-c-form-control pf-m-monospace"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg(
|
|
"Additional scopes to be passed to the OAuth Provider, separated by space. To replace existing scopes, prefix with *.",
|
|
)}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
</div>
|
|
</ak-form-group>
|
|
${this.renderUrlOptions()}
|
|
<ak-form-group expanded>
|
|
<span slot="header"> ${msg("OAuth Attribute mapping")} </span>
|
|
<div slot="body" class="pf-c-form">
|
|
<ak-form-element-horizontal
|
|
label=${msg("User Property Mappings")}
|
|
name="userPropertyMappings"
|
|
>
|
|
<ak-dual-select-dynamic-selected
|
|
.provider=${propertyMappingsProvider}
|
|
.selector=${propertyMappingsSelector(
|
|
this.instance?.userPropertyMappings,
|
|
)}
|
|
available-label="${msg("Available User Property Mappings")}"
|
|
selected-label="${msg("Selected User Property Mappings")}"
|
|
></ak-dual-select-dynamic-selected>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("Property mappings for user creation.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal
|
|
label=${msg("Group Property Mappings")}
|
|
name="groupPropertyMappings"
|
|
>
|
|
<ak-dual-select-dynamic-selected
|
|
.provider=${propertyMappingsProvider}
|
|
.selector=${propertyMappingsSelector(
|
|
this.instance?.groupPropertyMappings,
|
|
)}
|
|
available-label="${msg("Available Group Property Mappings")}"
|
|
selected-label="${msg("Selected Group Property Mappings")}"
|
|
></ak-dual-select-dynamic-selected>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("Property mappings for group creation.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
</div>
|
|
</ak-form-group>
|
|
<ak-form-group>
|
|
<span slot="header"> ${msg("Flow settings")} </span>
|
|
<div slot="body" class="pf-c-form">
|
|
<ak-form-element-horizontal
|
|
label=${msg("Authentication flow")}
|
|
name="authenticationFlow"
|
|
>
|
|
<ak-source-flow-search
|
|
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
|
.currentFlow=${this.instance?.authenticationFlow}
|
|
.instanceId=${this.instance?.pk}
|
|
fallback="default-source-authentication"
|
|
></ak-source-flow-search>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("Flow to use when authenticating existing users.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
<ak-form-element-horizontal
|
|
label=${msg("Enrollment flow")}
|
|
name="enrollmentFlow"
|
|
>
|
|
<ak-source-flow-search
|
|
flowType=${FlowsInstancesListDesignationEnum.Enrollment}
|
|
.currentFlow=${this.instance?.enrollmentFlow}
|
|
.instanceId=${this.instance?.pk}
|
|
fallback="default-source-enrollment"
|
|
></ak-source-flow-search>
|
|
<p class="pf-c-form__helper-text">
|
|
${msg("Flow to use when enrolling new users.")}
|
|
</p>
|
|
</ak-form-element-horizontal>
|
|
</div>
|
|
</ak-form-group>
|
|
<ak-form-group>
|
|
<span slot="header"> ${msg("Advanced settings")} </span>
|
|
<div slot="body" class="pf-c-form">
|
|
<ak-form-element-horizontal
|
|
label=${msg("Policy engine mode")}
|
|
required
|
|
name="policyEngineMode"
|
|
>
|
|
<ak-radio
|
|
.options=${policyEngineModes}
|
|
.value=${this.instance?.policyEngineMode}
|
|
>
|
|
</ak-radio>
|
|
</ak-form-element-horizontal>
|
|
</div>
|
|
</ak-form-group>`;
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ak-source-oauth-form": OAuthSourceForm;
|
|
}
|
|
}
|