Merge branch 'main' into web/legibility/dual-select-events

This commit is contained in:
Jens Langhammer
2025-02-25 09:32:42 +01:00
682 changed files with 41040 additions and 16055 deletions

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 800 800" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(3.21224,0,0,3.21224,-884.954,-920.291)">
<path d="M385.273,451.61C385.273,443.427 391.906,436.793 400.089,436.793C408.272,436.793 414.906,443.427 414.906,451.61L414.906,517.183C414.906,525.366 408.272,532 400.089,532C391.906,532 385.273,525.366 385.273,517.183L385.273,451.61Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(3.21224,0,0,3.21224,-884.954,-920.291)">
<path d="M494.028,362.311C488.889,350.162 481.534,339.25 472.164,329.883C462.794,320.516 451.885,313.16 439.736,308.019C427.157,302.699 413.794,300 400.024,300C386.253,300 372.89,302.699 360.311,308.019C348.163,313.158 337.251,320.513 327.883,329.883C318.516,339.253 311.16,350.162 306.019,362.311C300.699,374.89 298,388.253 298,402.023C298,415.794 300.699,429.156 306.019,441.735C311.158,453.884 318.513,464.796 327.883,474.163C337.251,483.531 348.163,490.886 360.311,496.027C364.238,497.689 368.242,499.094 372.309,500.242L372.309,490.738C358.128,486.326 345.133,478.518 334.332,467.717C316.786,450.171 307.122,426.839 307.122,402.023C307.122,377.207 316.786,353.879 334.332,336.329C351.879,318.783 375.21,309.119 400.026,309.119C424.843,309.119 448.171,318.783 465.72,336.329C483.267,353.876 492.93,377.207 492.93,402.023C492.93,426.839 483.267,450.168 465.72,467.717C455.065,478.373 442.272,486.115 428.314,490.558L428.314,500.074C432.184,498.957 436,497.612 439.741,496.03C451.89,490.892 462.802,483.536 472.17,474.166C481.537,464.799 488.892,453.887 494.033,441.738C499.354,429.156 502.053,415.796 502.053,402.026C502.053,388.256 499.354,374.893 494.033,362.314L494.028,362.311Z" style="fill:rgb(117,250,242);fill-rule:nonzero;"/>
</g>
<g transform="matrix(3.21224,0,0,3.21224,-884.954,-920.291)">
<path d="M450.18,402.024C450.18,374.366 427.68,351.867 400.023,351.867C372.365,351.867 349.866,374.366 349.866,402.024C349.866,419.841 359.205,435.518 373.246,444.418C374.052,441.388 375.355,438.558 377.062,436.022C366.164,428.638 358.985,416.153 358.985,402.027C358.985,379.399 377.395,360.989 400.023,360.989C422.65,360.989 441.06,379.399 441.06,402.027C441.06,415.985 434.053,428.339 423.371,435.754C425.11,438.273 426.444,441.086 427.284,444.107C441.055,435.156 450.18,419.639 450.18,402.027L450.18,402.024Z" style="fill:rgb(117,250,242);fill-rule:nonzero;"/>
</g>
<g transform="matrix(3.21224,0,0,3.21224,-884.954,-920.291)">
<path d="M476.399,402.023C476.399,381.624 468.453,362.442 454.027,348.019C439.602,333.593 420.422,325.648 400.023,325.648C379.624,325.648 360.442,333.593 346.019,348.019C331.594,362.445 323.648,381.624 323.648,402.023C323.648,422.422 331.594,441.604 346.019,456.027C353.631,463.639 362.568,469.444 372.309,473.231L372.309,463.334C365.013,460.034 358.299,455.409 352.469,449.578C339.764,436.876 332.771,419.986 332.771,402.02C332.771,384.055 339.767,367.167 352.469,354.463C365.17,341.758 382.061,334.765 400.026,334.765C417.992,334.765 434.88,341.761 447.584,354.463C460.289,367.164 467.282,384.055 467.282,402.02C467.282,419.986 460.286,436.874 447.584,449.578C441.904,455.258 435.387,459.792 428.314,463.075L428.314,473.003C437.832,469.213 446.57,463.488 454.033,456.027C468.459,441.601 476.404,422.422 476.404,402.023L476.399,402.023Z" style="fill:rgb(117,250,242);fill-rule:nonzero;"/>
</g>
<g transform="matrix(3.21224,0,0,3.21224,-884.954,-920.291)">
<path d="M400.024,419.692C410.097,419.692 418.263,411.526 418.263,401.453C418.263,391.38 410.097,383.214 400.024,383.214C389.951,383.214 381.785,391.38 381.785,401.453C381.785,411.526 389.951,419.692 400.024,419.692Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -74,7 +74,7 @@ const interfaces = [
["user/UserInterface.ts", "user"],
["flow/FlowInterface.ts", "flow"],
["standalone/api-browser/index.ts", "standalone/api-browser"],
["enterprise/rac/index.ts", "enterprise/rac"],
["rac/index.ts", "rac"],
["standalone/loading/index.ts", "standalone/loading"],
["polyfill/poly.ts", "."],
];
@ -88,9 +88,22 @@ const baseArgs = {
treeShaking: true,
external: ["*.woff", "*.woff2"],
tsconfig: "./tsconfig.json",
loader: { ".css": "text", ".md": "text" },
loader: {
".css": "text",
".md": "text",
".mdx": "text",
},
define: definitions,
format: "esm",
logOverride: {
/**
* HACK: Silences issue originating in ESBuild.
*
* @see {@link https://github.com/evanw/esbuild/blob/b914dd30294346aa15fcc04278f4b4b51b8b43b5/internal/logger/msg_ids.go#L211 ESBuild source}
* @expires 2025-08-11
*/
"invalid-source-url": "silent",
},
};
function getVersion() {

642
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
"@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2024.12.2-1736451530",
"@goauthentik/api": "^2025.2.0-1740418530",
"@lit-labs/ssr": "^3.2.2",
"@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2",
@ -30,12 +30,12 @@
"construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.38.1",
"country-flag-icons": "^1.5.13",
"dompurify": "^3.1.7",
"dompurify": "^3.2.4",
"fuse.js": "^7.0.0",
"guacamole-common-js": "^1.5.0",
"lit": "^3.2.0",
"md-front-matter": "^1.0.4",
"mermaid": "^11.2.1",
"mermaid": "^11.4.1",
"rapidoc": "^9.3.7",
"showdown": "^2.1.0",
"style-mod": "^4.1.2",
@ -73,7 +73,7 @@
"@wdio/spec-reporter": "^9.1.2",
"chokidar": "^4.0.1",
"chromedriver": "^131.0.1",
"esbuild": "^0.24.0",
"esbuild": "^0.25.0",
"eslint": "^9.11.1",
"eslint-plugin-lit": "^1.15.0",
"eslint-plugin-wc": "^2.1.1",
@ -125,6 +125,7 @@
"lint:nightmare": "wireit",
"lint:package": "wireit",
"lint:precommit": "wireit",
"lint:types": "wireit",
"lit-analyse": "wireit",
"postinstall": "bash scripts/patch-spotlight.sh",
"precommit": "wireit",

View File

@ -168,7 +168,7 @@ class IdentificationStage extends Stage<IdentificationChallenge> {
${
this.challenge.applicationPre
? `<p>
Login to continue to ${this.challenge.applicationPre}.
Log in to continue to ${this.challenge.applicationPre}.
</p>`
: ""
}

View File

@ -6,7 +6,7 @@ const config: KnipConfig = {
"./src/user/UserInterface.ts",
"./src/flow/FlowInterface.ts",
"./src/standalone/api-browser/index.ts",
"./src/enterprise/rac/index.ts",
"./src/rac/index.ts",
"./src/standalone/loading/index.ts",
"./src/polyfill/poly.ts",
],

View File

@ -66,6 +66,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
class="pf-c-backdrop"
@click=${(e: PointerEvent) => {
e.stopPropagation();
this.closeModal();
}}
>
<div class="pf-l-bullseye">

View File

@ -10,7 +10,8 @@ import { me } from "@goauthentik/common/users";
import { WebsocketClient } from "@goauthentik/common/ws";
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
import "@goauthentik/elements/ak-locale-context";
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
import "@goauthentik/elements/banner/VersionBanner";
import "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/elements/notifications/APIDrawer";
@ -72,7 +73,8 @@ export class AdminInterface extends AuthenticatedInterface {
:host([theme="dark"]) .pf-c-page {
--pf-c-page--BackgroundColor: var(--ak-dark-background);
}
ak-enterprise-status {
ak-enterprise-status,
ak-version-banner {
grid-area: header;
}
ak-admin-sidebar {
@ -128,6 +130,7 @@ export class AdminInterface extends AuthenticatedInterface {
return html` <ak-locale-context>
<div class="pf-c-page">
<ak-enterprise-status interface="admin"></ak-enterprise-status>
<ak-version-banner></ak-version-banner>
<ak-admin-sidebar
class="pf-c-page__sidebar ${classMap(sidebarClasses)}"
></ak-admin-sidebar>

View File

@ -1,6 +1,4 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_SIDEBAR_TOGGLE, VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import {
@ -12,12 +10,12 @@ import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/R
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
import { spread } from "@open-wc/lit-helpers";
import { msg, str } from "@lit/localize";
import { msg } from "@lit/localize";
import { TemplateResult, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { map } from "lit/directives/map.js";
import { CoreApi, UiThemeEnum } from "@goauthentik/api";
import { UiThemeEnum } from "@goauthentik/api";
import type { SessionUser, UserSelf } from "@goauthentik/api";
@customElement("ak-admin-sidebar")
@ -108,7 +106,6 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement
// prettier-ignore
const sidebarContent: SidebarEntry[] = [
[`${globalAK().api.base}if/user/`, msg("User interface"), { "?isAbsoluteLink": true, "?highlight": true }],
[null, msg("Dashboards"), { "?expanded": true }, [
["/administration/overview", msg("Overview")],
["/administration/dashboard/users", msg("User Statistics")],
@ -162,40 +159,11 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement
// prettier-ignore
return html`
${this.renderNewVersionMessage()}
${this.renderImpersonationMessage()}
${map(sidebarContent, renderOneSidebarItem)}
${this.renderEnterpriseMenu()}
`;
}
renderNewVersionMessage() {
return this.version && this.version.versionCurrent !== VERSION
? html`
<ak-sidebar-item ?highlight=${true}>
<span slot="label">${msg("A newer version of the UI is available.")}</span>
</ak-sidebar-item>
`
: nothing;
}
renderImpersonationMessage() {
const reload = () =>
new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve().then(() => {
window.location.reload();
});
return this.impersonation
? html`<ak-sidebar-item ?highlight=${true} @click=${reload}>
<span slot="label"
>${msg(
str`You're currently impersonating ${this.impersonation}. Click to stop.`,
)}</span
>
</ak-sidebar-item>`
: nothing;
}
renderEnterpriseMenu() {
return this.can(CapabilitiesEnum.IsEnterprise)
? html`

View File

@ -74,10 +74,11 @@ export class FooterLinkInput extends AkControlElement<FooterLink> {
tabindex="1"
/>
<input
type="text"
type="url"
@change=${onChange}
value="${ifDefined(this.footerLink.href ?? undefined)}"
class="pf-c-form-control ak-form-control"
class="pf-c-form-control ak-form-control pf-m-monospace"
autocomplete="off"
required
placeholder=${msg("URL")}
name="href"

View File

@ -70,6 +70,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
name="avatars"
label=${msg("Avatars")}
value="${ifDefined(this._settings?.avatars)}"
inputHint="code"
.bighelp=${html`
<p class="pf-c-form__helper-text">
${msg(
@ -156,6 +157,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
<ak-text-input
name="eventRetention"
label=${msg("Event retention")}
inputHint="code"
required
value="${ifDefined(this._settings?.eventRetention)}"
.bighelp=${html`<p class="pf-c-form__helper-text">
@ -163,7 +165,8 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
</p>
<p class="pf-c-form__helper-text">
${msg(
'When using an external logging solution for archiving, this can be set to "minutes=5".',
html`When using an external logging solution for archiving, this can be
set to <code>minutes=5</code>.`,
)}
</p>
<p class="pf-c-form__helper-text">
@ -218,6 +221,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
<ak-text-input
name="defaultTokenDuration"
label=${msg("Default token duration")}
inputHint="code"
required
value="${ifDefined(this._settings?.defaultTokenDuration)}"
.bighelp=${html`<p class="pf-c-form__helper-text">

View File

@ -7,6 +7,7 @@ import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
import "@goauthentik/elements/Alert.js";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
@ -21,7 +22,7 @@ import "@goauthentik/elements/forms/SearchSelect";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { TemplateResult, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
@ -120,7 +121,12 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
}
renderForm(): TemplateResult {
const alertMsg = msg(
"Using this form will only create an Application. In order to authenticate with the application, you will have to manually pair it with a Provider.",
);
return html`<form class="pf-c-form pf-m-horizontal">
${this.instance ? nothing : html`<ak-alert level="pf-m-info">${alertMsg}</ak-alert>`}
<ak-text-input
name="name"
value=${ifDefined(this.instance?.name)}
@ -134,6 +140,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
label=${msg("Slug")}
required
help=${msg("Internal application name used in URLs.")}
inputHint="code"
></ak-text-input>
<ak-text-input
name="group"
@ -142,6 +149,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
help=${msg(
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
)}
inputHint="code"
></ak-text-input>
<ak-provider-search-input
name="provider"
@ -182,6 +190,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
help=${msg(
"If left empty, authentik will try to extract the launch URL based on the selected provider.",
)}
inputHint="code"
></ak-text-input>
<ak-switch-input
name="openInNewTab"

View File

@ -50,7 +50,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
}
pageDescription(): string {
return msg(
str`External applications that use ${this.brand.brandingTitle || "authentik"} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
str`External applications that use ${this.brand?.brandingTitle ?? "authentik"} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
);
}
pageIcon(): string {
@ -85,10 +85,6 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
];
}
renderSectionBefore(): TemplateResult {
return html`<ak-application-wizard-hint></ak-application-wizard-hint>`;
}
renderSidebarAfter(): TemplateResult {
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
<div class="pf-c-card">
@ -160,12 +156,21 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
}
renderObjectCreate(): TemplateResult {
return html`<ak-forms-modal .open=${getURLParam("createForm", false)}>
<span slot="submit"> ${msg("Create")} </span>
<span slot="header"> ${msg("Create Application")} </span>
<ak-application-form slot="form"> </ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
</ak-forms-modal>`;
return html` <ak-application-wizard .open=${getURLParam("createWizard", false)}>
<button
slot="trigger"
class="pf-c-button pf-m-primary"
data-ouia-component-id="start-application-wizard"
>
${msg("Create with Provider")}
</button>
</ak-application-wizard>
<ak-forms-modal .open=${getURLParam("createForm", false)}>
<span slot="submit"> ${msg("Create")} </span>
<span slot="header"> ${msg("Create Application")} </span>
<ak-application-form slot="form"> </ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
</ak-forms-modal>`;
}
}

View File

@ -80,8 +80,8 @@ export class ApplicationViewPage extends AKElement {
if (
app.providerObj &&
[
RbacPermissionsAssignedByUsersListModelEnum.ProvidersProxyProxyprovider.toString(),
RbacPermissionsAssignedByUsersListModelEnum.ProvidersLdapLdapprovider.toString(),
RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersProxyProxyprovider.toString(),
RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersLdapLdapprovider.toString(),
].includes(app.providerObj.metaModelName)
) {
this.fetchIsMissingOutpost([app.provider || 0]);
@ -340,7 +340,7 @@ export class ApplicationViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.CoreApplication}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikCoreApplication}
objectPk=${this.application.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -86,7 +86,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
</button>
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.CoreApplicationentitlement}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikCoreApplicationentitlement}
objectPk=${item.pbmUuid}
>
</ak-rbac-object-permission-modal>`,

View File

@ -30,7 +30,7 @@ export class ApplicationWizardStep extends WizardStep {
// As recommended in [WizardStep](../../../components/ak-wizard/WizardStep.ts), we override
// these fields and provide them to all the child classes.
wizardTitle = msg("New application");
wizardDescription = msg("Create a new application");
wizardDescription = msg("Create a new application and configure a provider for it.");
canCancel = true;
// This should be overridden in the children for more precise targeting.

View File

@ -142,11 +142,11 @@ function renderLDAPOverview(rawProvider: OneOfProvider) {
const providerName = (p: ProviderModelEnum): string => p.toString().split(".")[1];
export const providerRenderers = new Map([
[providerName(ProviderModelEnum.SamlSamlprovider), renderSAMLOverview],
[providerName(ProviderModelEnum.ScimScimprovider), renderSCIMOverview],
[providerName(ProviderModelEnum.RadiusRadiusprovider), renderRadiusOverview],
[providerName(ProviderModelEnum.RacRacprovider), renderRACOverview],
[providerName(ProviderModelEnum.ProxyProxyprovider), renderProxyOverview],
[providerName(ProviderModelEnum.Oauth2Oauth2provider), renderOAuth2Overview],
[providerName(ProviderModelEnum.LdapLdapprovider), renderLDAPOverview],
[providerName(ProviderModelEnum.AuthentikProvidersSamlSamlprovider), renderSAMLOverview],
[providerName(ProviderModelEnum.AuthentikProvidersScimScimprovider), renderSCIMOverview],
[providerName(ProviderModelEnum.AuthentikProvidersRadiusRadiusprovider), renderRadiusOverview],
[providerName(ProviderModelEnum.AuthentikProvidersRacRacprovider), renderRACOverview],
[providerName(ProviderModelEnum.AuthentikProvidersProxyProxyprovider), renderProxyOverview],
[providerName(ProviderModelEnum.AuthentikProvidersOauth2Oauth2provider), renderOAuth2Overview],
[providerName(ProviderModelEnum.AuthentikProvidersLdapLdapprovider), renderLDAPOverview],
]);

View File

@ -128,6 +128,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
?invalid=${errors.slug ?? this.errors.has("slug")}
.errorMessages=${this.errorMessages("slug")}
help=${msg("Internal application name used in URLs.")}
inputHint="code"
></ak-slug-input>
<ak-text-input
name="group"
@ -137,6 +138,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
help=${msg(
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
)}
inputHint="code"
></ak-text-input>
<ak-radio-input
label=${msg("Policy engine mode")}
@ -159,6 +161,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
help=${msg(
"If left empty, authentik will try to extract the launch URL based on the selected provider.",
)}
inputHint="code"
></ak-text-input>
<ak-switch-input
name="openInNewTab"

View File

@ -25,7 +25,7 @@ export class ApplicationWizardProviderForm<T extends OneOfProvider> extends AKEl
wizard!: ApplicationWizardState;
@property({ type: Object, attribute: false })
errors: Map<string | number | symbol, string> = new Map();
errors: Record<string | number | symbol, string> = {};
@query("form#providerform")
form!: HTMLFormElement;
@ -41,13 +41,13 @@ export class ApplicationWizardProviderForm<T extends OneOfProvider> extends AKEl
}
get valid() {
this.errors = new Map();
this.errors = {};
return this.form.checkValidity();
}
errorMessages(name: string) {
return this.errors.has(name)
? [this.errors.get(name)]
return name in this.errors
? [this.errors[name]]
: (this.wizard.errors?.provider?.[name] ??
this.wizard.errors?.provider?.[camelToSnake(name)] ??
[]);
@ -56,7 +56,7 @@ export class ApplicationWizardProviderForm<T extends OneOfProvider> extends AKEl
isValid(name: keyof T) {
return !(
(this.wizard.errors?.provider?.[name as string] ?? []).length > 0 ||
this.errors.has(name)
this.errors?.[name] !== undefined
);
}
}

View File

@ -57,6 +57,7 @@ export class ApplicationWizardRACProviderForm extends ApplicationWizardProviderF
help=${msg(
"Determines how long a session lasts before being disconnected and requiring re-authorization.",
)}
inputHint="code"
></ak-text-input>
<ak-form-group .expanded=${true}>

View File

@ -155,7 +155,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
</button>
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.BlueprintsBlueprintinstance}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikBlueprintsBlueprintinstance}
objectPk=${item.pk}
>
</ak-rbac-object-permission-modal>

View File

@ -59,7 +59,10 @@ export class BrandForm extends ModelForm<Brand, string> {
<input
type="text"
value="${first(this.instance?.domain, window.location.host)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
inputmode="url"
required
/>
<p class="pf-c-form__helper-text">
@ -116,7 +119,9 @@ export class BrandForm extends ModelForm<Brand, string> {
<input
type="text"
value="${first(this.instance?.brandingLogo, DefaultBrand.brandingLogo)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
@ -134,7 +139,9 @@ export class BrandForm extends ModelForm<Brand, string> {
this.instance?.brandingFavicon,
DefaultBrand.brandingFavicon,
)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">

View File

@ -93,7 +93,7 @@ export class BrandListPage extends TablePage<Brand> {
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.BrandsBrand}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikBrandsBrand}
objectPk=${item.brandUuid}
>
</ak-rbac-object-permission-modal>`,

View File

@ -52,7 +52,13 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
?writeOnly=${this.instance !== undefined}
?required=${true}
>
<textarea class="pf-c-form-control" required></textarea>
<textarea
autocomplete="off"
spellcheck="false"
class="pf-c-form-control pf-m-monospace"
placeholder="-----BEGIN CERTIFICATE-----"
required
></textarea>
<p class="pf-c-form__helper-text">${msg("PEM-encoded Certificate data.")}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
@ -60,7 +66,11 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
?writeOnly=${this.instance !== undefined}
label=${msg("Private Key")}
>
<textarea class="pf-c-form-control"></textarea>
<textarea
autocomplete="off"
class="pf-c-form-control pf-m-monospace"
spellcheck="false"
></textarea>
<p class="pf-c-form__helper-text">
${msg(
"Optional Private Key. If this is set, you can use this keypair for encryption.",

View File

@ -134,7 +134,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
</button>
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.CryptoCertificatekeypair}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikCryptoCertificatekeypair}
objectPk=${item.pk}
>
</ak-rbac-object-permission-modal>`,

View File

@ -51,13 +51,26 @@ export class EnterpriseLicenseForm extends ModelForm<License, string> {
}
renderForm(): TemplateResult {
// prettier-ignore
return html`
<ak-form-element-horizontal label=${msg("Install ID")}>
<input class="pf-c-form-control" readonly type="text" value="${ifDefined(this.installID)}" />
return html` <ak-form-element-horizontal label=${msg("Install ID")}>
<input
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
readonly
type="text"
value="${ifDefined(this.installID)}"
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="key" ?writeOnly=${this.instance !== undefined} label=${msg("License key")}>
<textarea class="pf-c-form-control"></textarea>
<ak-form-element-horizontal
name="key"
?writeOnly=${this.instance !== undefined}
label=${msg("License key")}
>
<textarea
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
></textarea>
</ak-form-element-horizontal>`;
}
}

View File

@ -231,7 +231,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
</button>
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.EnterpriseLicense}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikEnterpriseLicense}
objectPk=${item.licenseUuid}
>
</ak-rbac-object-permission-modal> `,
@ -254,7 +254,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
const renderCard = (installID: string) => html`
<div class="pf-c-card__title">${msg("Your Install ID")}</div>
<div class="pf-c-card__body install-id">${installID}</div>
<div class="pf-c-card__body install-id pf-m-monospace">${installID}</div>
<div class="pf-c-card__body">
<a
target="_blank"

View File

@ -99,7 +99,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.EventsNotificationrule}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikEventsNotificationrule}
objectPk=${item.pk}
>
</ak-rbac-object-permission-modal>`,

View File

@ -94,7 +94,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.EventsNotificationtransport}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikEventsNotificationtransport}
objectPk=${item.pk}
>
</ak-rbac-object-permission-modal>

View File

@ -99,7 +99,9 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
<input
type="text"
value="${ifDefined(this.instance?.slug)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">${msg("Visible in the URL.")}</p>

View File

@ -191,7 +191,7 @@ export class FlowViewPage extends AKElement {
const finalURL = `${
link.link
}?${encodeURI(
`inspector&next=/#${window.location.hash}`,
`inspector=open&next=/#${window.location.hash}`,
)}`;
window.open(finalURL, "_blank");
})
@ -280,7 +280,7 @@ export class FlowViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.FlowsFlow}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikFlowsFlow}
objectPk=${this.flow.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -203,7 +203,7 @@ export class GroupViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.CoreGroup}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikCoreGroup}
objectPk=${this.group.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -148,7 +148,7 @@ export class OutpostListPage extends TablePage<Outpost> {
</button>
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.OutpostsOutpost}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikOutpostsOutpost}
objectPk=${item.pk}
>
</ak-rbac-object-permission-modal>

View File

@ -72,12 +72,17 @@ export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnecti
<input
type="text"
value="${ifDefined(this.instance?.url)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
inputmode="url"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"Can be in the format of 'unix://' when connecting to a local docker daemon, using 'ssh://' to connect via SSH, or 'https://:2376' when connecting to a remote system.",
html`Can be in the format of <code>unix://</code> when connecting to a local
docker daemon, using <code>ssh://</code> to connect via SSH, or
<code>https://:2376</code> when connecting to a remote system.`,
)}
</p>
</ak-form-element-horizontal>

View File

@ -31,9 +31,9 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
@property({ type: Array })
allowedTypes: PolicyBindingCheckTarget[] = [
PolicyBindingCheckTarget.policy,
PolicyBindingCheckTarget.group,
PolicyBindingCheckTarget.user,
PolicyBindingCheckTarget.policy,
];
@property({ type: Array })

View File

@ -58,9 +58,9 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
@property({ type: Array })
allowedTypes: PolicyBindingCheckTarget[] = [
PolicyBindingCheckTarget.policy,
PolicyBindingCheckTarget.group,
PolicyBindingCheckTarget.user,
PolicyBindingCheckTarget.policy,
];
@property({ type: Array })

View File

@ -52,6 +52,21 @@ export class PolicyWizard extends AKElement {
});
}
selectListener = ({ detail }: CustomEvent<TypeCreate>) => {
if (!this.wizard) return;
const { component, modelName } = detail;
const idx = this.wizard.steps.indexOf("initial") + 1;
// Exclude all current steps starting with type-,
// this happens when the user selects a type and then goes back
this.wizard.steps = this.wizard.steps.filter((step) => !step.startsWith("type-"));
this.wizard.steps.splice(idx, 0, `type-${component}-${modelName}`);
this.wizard.isValid = true;
};
render(): TemplateResult {
return html`
<ak-wizard
@ -62,23 +77,10 @@ export class PolicyWizard extends AKElement {
<ak-wizard-page-type-create
slot="initial"
.types=${this.policyTypes}
@select=${(ev: CustomEvent<TypeCreate>) => {
if (!this.wizard) return;
const idx = this.wizard.steps.indexOf("initial") + 1;
// Exclude all current steps starting with type-,
// this happens when the user selects a type and then goes back
this.wizard.steps = this.wizard.steps.filter(
(step) => !step.startsWith("type-"),
);
this.wizard.steps.splice(
idx,
0,
`type-${ev.detail.component}-${ev.detail.modelName}`,
);
this.wizard.isValid = true;
}}
@select=${this.selectListener}
>
</ak-wizard-page-type-create>
${this.policyTypes.map((type) => {
return html`
<ak-wizard-page-form

View File

@ -113,7 +113,9 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
<input
type="text"
value="${ifDefined(this.instance?.clientIp || "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
<p class="pf-c-form__helper-text">
${msg(

View File

@ -1,5 +1,6 @@
import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/ak-dual-select";
import { DataProvision, DualSelectPair } from "@goauthentik/elements/ak-dual-select/types";
import "@goauthentik/elements/forms/FormGroup";
@ -46,7 +47,7 @@ export class GeoIPPolicyForm extends BasePolicyForm<GeoIPPolicy> {
}
renderForm(): TemplateResult {
return html` <span>
return html`<span>
${msg(
"Ensure the user satisfies requirements of geography or network topology, based on IP address. If any of the configured values match, the policy passes.",
)}
@ -79,14 +80,123 @@ export class GeoIPPolicyForm extends BasePolicyForm<GeoIPPolicy> {
)}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Policy-specific settings")} </span>
<ak-form-group>
<span slot="header"> ${msg("Distance settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal name="checkHistoryDistance">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${this.instance?.checkHistoryDistance ?? false}
/>
<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("Check historical distance of logins")}</span
>
</label>
<p class="pf-c-form__helper-text">
${msg(
"When this option enabled, the GeoIP data of the policy request is compared to the specified number of historical logins.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Maximum distance")}
name="historyMaxDistanceKm"
>
<input
type="number"
min="1"
value="${first(this.instance?.historyMaxDistanceKm, 100)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${msg(
"Maximum distance a login attempt is allowed from in kilometers.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Distance tolerance")}
name="distanceToleranceKm"
>
<input
type="number"
min="1"
value="${first(this.instance?.distanceToleranceKm, 50)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${msg("Tolerance in checking for distances in kilometers.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Historical Login Count")}
name="historyLoginCount"
>
<input
type="number"
min="1"
value="${first(this.instance?.historyLoginCount, 5)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${msg("Amount of previous login events to check against.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="checkImpossibleTravel">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${this.instance?.checkImpossibleTravel ?? 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("Check impossible travel")}</span
>
</label>
<p class="pf-c-form__helper-text">
${msg(
"When this option enabled, the GeoIP data of the policy request is compared to the specified number of historical logins and if the travel would have been possible in the amount of time since the previous event.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Impossible travel tolerance")}
name="impossibleToleranceKm"
>
<input
type="number"
min="1"
value="${first(this.instance?.impossibleToleranceKm, 50)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${msg("Tolerance in checking for distances in kilometers.")}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>
<ak-form-group>
<span slot="header">${msg("Static rule settings")}</span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal label=${msg("ASNs")} name="asns">
<input
type="text"
value="${this.instance?.asns ?? ""}"
class="pf-c-form-control"
value="${this.instance?.asns?.join(",") ?? ""}"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
<p class="pf-c-form__helper-text">
${msg(

View File

@ -93,7 +93,7 @@ export class ReputationListPage extends TablePage<Reputation> {
<small>${item.updated.toLocaleString()}</small>`,
html`
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.PoliciesReputationReputationpolicy}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikPoliciesReputationReputationpolicy}
objectPk=${item.pk || ""}
>
</ak-rbac-object-permission-modal>

View File

@ -10,7 +10,7 @@ import { LDAPSourcePropertyMapping, PropertymappingsApi } from "@goauthentik/api
@customElement("ak-property-mapping-source-ldap-form")
export class PropertyMappingSourceLDAPForm extends BasePropertyMappingForm<LDAPSourcePropertyMapping> {
docLink(): string {
return "/docs/user-sources/sources/property-mappings/expressions?utm_source=authentik";
return "/docs/users-sources/sources/property-mappings/expressions?utm_source=authentik";
}
loadInstance(pk: string): Promise<LDAPSourcePropertyMapping> {

View File

@ -10,7 +10,7 @@ import { OAuthSourcePropertyMapping, PropertymappingsApi } from "@goauthentik/ap
@customElement("ak-property-mapping-source-oauth-form")
export class PropertyMappingSourceOAuthForm extends BasePropertyMappingForm<OAuthSourcePropertyMapping> {
docLink(): string {
return "/docs/user-sources/sources/property-mappings/expressions?utm_source=authentik";
return "/docs/users-sources/sources/property-mappings/expressions?utm_source=authentik";
}
loadInstance(pk: string): Promise<OAuthSourcePropertyMapping> {

View File

@ -10,7 +10,7 @@ import { PlexSourcePropertyMapping, PropertymappingsApi } from "@goauthentik/api
@customElement("ak-property-mapping-source-plex-form")
export class PropertyMappingSourcePlexForm extends BasePropertyMappingForm<PlexSourcePropertyMapping> {
docLink(): string {
return "/docs/user-sources/sources/property-mappings/expressions?utm_source=authentik";
return "/docs/users-sources/sources/property-mappings/expressions?utm_source=authentik";
}
loadInstance(pk: string): Promise<PlexSourcePropertyMapping> {

View File

@ -10,7 +10,7 @@ import { PropertymappingsApi, SAMLSourcePropertyMapping } from "@goauthentik/api
@customElement("ak-property-mapping-source-saml-form")
export class PropertyMappingSourceSAMLForm extends BasePropertyMappingForm<SAMLSourcePropertyMapping> {
docLink(): string {
return "/docs/user-sources/sources/property-mappings/expressions?utm_source=authentik";
return "/docs/users-sources/sources/property-mappings/expressions?utm_source=authentik";
}
loadInstance(pk: string): Promise<SAMLSourcePropertyMapping> {

View File

@ -10,7 +10,7 @@ import { PropertymappingsApi, SCIMSourcePropertyMapping } from "@goauthentik/api
@customElement("ak-property-mapping-source-scim-form")
export class PropertyMappingSourceSCIMForm extends BasePropertyMappingForm<SCIMSourcePropertyMapping> {
docLink(): string {
return "/docs/user-sources/sources/property-mappings/expressions?utm_source=authentik";
return "/docs/users-sources/sources/property-mappings/expressions?utm_source=authentik";
}
loadInstance(pk: string): Promise<SCIMSourcePropertyMapping> {

View File

@ -71,7 +71,7 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
renderExampleButtons() {
return this.mapping?.metaModelName ===
RbacPermissionsAssignedByUsersListModelEnum.SourcesLdapLdapsourcepropertymapping
RbacPermissionsAssignedByUsersListModelEnum.AuthentikSourcesLdapLdapsourcepropertymapping
? html`<p>${msg("Example context data")}</p>
${this.renderExampleLDAP()}`
: nothing;

View File

@ -9,6 +9,7 @@ import "@goauthentik/admin/providers/rac/RACProviderForm";
import "@goauthentik/admin/providers/radius/RadiusProviderForm";
import "@goauthentik/admin/providers/saml/SAMLProviderForm";
import "@goauthentik/admin/providers/scim/SCIMProviderForm";
import "@goauthentik/admin/providers/ssf/SSFProviderFormPage";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/DeleteBulkForm";

View File

@ -7,6 +7,7 @@ import "@goauthentik/admin/providers/rac/RACProviderViewPage";
import "@goauthentik/admin/providers/radius/RadiusProviderViewPage";
import "@goauthentik/admin/providers/saml/SAMLProviderViewPage";
import "@goauthentik/admin/providers/scim/SCIMProviderViewPage";
import "@goauthentik/admin/providers/ssf/SSFProviderViewPage";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/EmptyState";
@ -80,6 +81,10 @@ export class ProviderViewPage extends AKElement {
return html`<ak-provider-microsoft-entra-view
providerID=${ifDefined(this.provider.pk)}
></ak-provider-microsoft-entra-view>`;
case "ak-provider-ssf-form":
return html`<ak-provider-ssf-view
providerID=${ifDefined(this.provider.pk)}
></ak-provider-ssf-view>`;
default:
return html`<p>Invalid provider type ${this.provider?.component}</p>`;
}

View File

@ -82,7 +82,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
<input
type="email"
value="${first(this.instance?.delegatedSubject, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
required
/>
<p class="pf-c-form__helper-text">
@ -99,7 +99,7 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
<input
type="text"
value="${first(this.instance?.defaultGroupEmailDomain, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
required
/>
<p class="pf-c-form__helper-text">

View File

@ -35,7 +35,7 @@ export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProvi
<span slot="header">${msg("Sync Group")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.Group}
model=${SyncObjectModelEnum.AuthentikCoreModelsGroup}
.sync=${(data: ProvidersGoogleWorkspaceSyncObjectCreateRequest) => {
return new ProvidersApi(
DEFAULT_CONFIG,

View File

@ -35,7 +35,7 @@ export class GoogleWorkspaceProviderUserList extends Table<GoogleWorkspaceProvid
<span slot="header">${msg("Sync User")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.User}
model=${SyncObjectModelEnum.AuthentikCoreModelsUser}
.sync=${(data: ProvidersGoogleWorkspaceSyncObjectCreateRequest) => {
return new ProvidersApi(
DEFAULT_CONFIG,

View File

@ -16,7 +16,6 @@ import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
@ -51,7 +50,6 @@ export class GoogleWorkspaceProviderViewPage extends AKElement {
return [
PFBase,
PFButton,
PFBanner,
PFForm,
PFFormControl,
PFStack,
@ -147,7 +145,7 @@ export class GoogleWorkspaceProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersGoogleWorkspaceGoogleworkspaceprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersGoogleWorkspaceGoogleworkspaceprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;
@ -157,11 +155,7 @@ export class GoogleWorkspaceProviderViewPage extends AKElement {
if (!this.provider) {
return html``;
}
return html`<div slot="header" class="pf-c-banner pf-m-info">
${msg("Google Workspace Provider is in preview.")}
<a href="mailto:hello+feature/gws@goauthentik.io">${msg("Send us feedback!")}</a>
</div>
${!this.provider?.assignedBackchannelApplicationName
return html`${!this.provider?.assignedBackchannelApplicationName
? html`<div slot="header" class="pf-c-banner pf-m-warning">
${msg(
"Warning: Provider is not assigned to an application as backchannel provider.",

View File

@ -127,6 +127,7 @@ export function renderForm(
label=${msg("Base DN")}
required
value="${provider?.baseDn ?? "DC=ldap,DC=goauthentik,DC=io"}"
inputHint="code"
.errorMessages=${errors?.baseDn ?? []}
help=${msg(
"LDAP DN under which bind requests and search requests can be made.",
@ -153,6 +154,7 @@ export function renderForm(
value="${provider?.tlsServerName ?? ""}"
.errorMessages=${errors?.tlsServerName ?? []}
help=${tlsServerNameHelp}
inputHint="code"
></ak-text-input>
<ak-number-input

View File

@ -111,7 +111,7 @@ export class LDAPProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersLdapLdapprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersLdapLdapprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -67,7 +67,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
<input
type="text"
value="${first(this.instance?.clientId, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
required
/>
<p class="pf-c-form__helper-text">
@ -82,7 +82,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
<input
type="text"
value="${first(this.instance?.clientSecret, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
required
/>
<p class="pf-c-form__helper-text">
@ -97,7 +97,7 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
<input
type="text"
value="${first(this.instance?.tenantId, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
required
/>
<p class="pf-c-form__helper-text">

View File

@ -32,7 +32,7 @@ export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProvide
<span slot="header">${msg("Sync Group")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.Group}
model=${SyncObjectModelEnum.AuthentikCoreModelsGroup}
.sync=${(data: ProvidersMicrosoftEntraSyncObjectCreateRequest) => {
return new ProvidersApi(
DEFAULT_CONFIG,

View File

@ -35,7 +35,7 @@ export class MicrosoftEntraProviderUserList extends Table<MicrosoftEntraProvider
<span slot="header">${msg("Sync User")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.User}
model=${SyncObjectModelEnum.AuthentikCoreModelsUser}
.sync=${(data: ProvidersMicrosoftEntraSyncObjectCreateRequest) => {
return new ProvidersApi(
DEFAULT_CONFIG,

View File

@ -16,7 +16,6 @@ import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
@ -51,7 +50,6 @@ export class MicrosoftEntraProviderViewPage extends AKElement {
return [
PFBase,
PFButton,
PFBanner,
PFForm,
PFFormControl,
PFStack,
@ -147,7 +145,7 @@ export class MicrosoftEntraProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersMicrosoftEntraMicrosoftentraprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersMicrosoftEntraMicrosoftentraprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;
@ -157,11 +155,7 @@ export class MicrosoftEntraProviderViewPage extends AKElement {
if (!this.provider) {
return html``;
}
return html`<div slot="header" class="pf-c-banner pf-m-info">
${msg("Microsoft Entra Provider is in preview.")}
<a href="mailto:hello+feature/mse@goauthentik.io">${msg("Send us feedback!")}</a>
</div>
${!this.provider?.assignedBackchannelApplicationName
return html`${!this.provider?.assignedBackchannelApplicationName
? html`<div slot="header" class="pf-c-banner pf-m-warning">
${msg(
"Warning: Provider is not assigned to an application as backchannel provider.",

View File

@ -163,6 +163,7 @@ export function renderForm(
label=${msg("Client ID")}
value="${first(provider?.clientId, randomString(40, ascii_letters + digits))}"
required
inputHint="code"
>
</ak-text-input>
<ak-text-input
@ -172,6 +173,7 @@ export function renderForm(
provider?.clientSecret,
randomString(128, ascii_letters + digits),
)}"
inputHint="code"
?hidden=${!showClientSecret}
>
</ak-text-input>
@ -253,6 +255,7 @@ export function renderForm(
<ak-text-input
name="accessCodeValidity"
label=${msg("Access code validity")}
inputHint="code"
required
value="${first(provider?.accessCodeValidity, "minutes=1")}"
.bighelp=${html`<p class="pf-c-form__helper-text">
@ -265,6 +268,7 @@ export function renderForm(
name="accessTokenValidity"
label=${msg("Access Token validity")}
value="${first(provider?.accessTokenValidity, "minutes=5")}"
inputHint="code"
required
.bighelp=${html` <p class="pf-c-form__helper-text">
${msg("Configure how long access tokens are valid for.")}
@ -277,6 +281,7 @@ export function renderForm(
name="refreshTokenValidity"
label=${msg("Refresh Token validity")}
value="${first(provider?.refreshTokenValidity, "days=30")}"
inputHint="code"
?required=${true}
.bighelp=${html` <p class="pf-c-form__helper-text">
${msg("Configure how long refresh tokens are valid for.")}

View File

@ -80,7 +80,9 @@ export class OAuth2ProviderRedirectURI extends AkControlElement<RedirectURI> {
type="text"
@change=${onChange}
value="${ifDefined(this.redirectURI.url ?? undefined)}"
class="pf-c-form-control ak-form-control"
class="pf-c-form-control ak-form-control pf-m-monospace"
spellcheck="false"
autocomplete="off"
required
id="url"
placeholder=${msg("URL")}

View File

@ -4,7 +4,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import renderDescriptionList from "@goauthentik/components/DescriptionList";
import "@goauthentik/components/events/ObjectChangelog";
import MDProviderOAuth2 from "@goauthentik/docs/add-secure-apps/providers/oauth2/index.md";
import MDProviderOAuth2 from "@goauthentik/docs/add-secure-apps/providers/oauth2/index.mdx";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/EmptyState";
@ -158,7 +158,7 @@ export class OAuth2ProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersOauth2Oauth2provider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersOauth2Oauth2provider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;
@ -175,7 +175,7 @@ export class OAuth2ProviderViewPage extends AKElement {
</div>`}
<div class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
<div
class="pf-c-card pf-l-grid__item pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl"
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl"
>
<div class="pf-c-card__body">
<dl class="pf-c-description-list">
@ -369,7 +369,6 @@ export class OAuth2ProviderViewPage extends AKElement {
]}
.md=${MDProviderOAuth2}
meta="providers/oauth2/index.md"
;
></ak-markdown>
</div>
</div>

View File

@ -4,7 +4,7 @@ import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types";
import { OAuthSource, SourcesApi } from "@goauthentik/api";
const sourceToSelect = (source: OAuthSource) => [
source.slug,
source.pk,
`${source.name} (${source.slug})`,
source.name,
source,
@ -37,13 +37,15 @@ export function oauth2SourcesSelector(instanceMappings?: string[]) {
const oauthSources = new SourcesApi(DEFAULT_CONFIG);
const mappings = await Promise.allSettled(
instanceMappings.map((instanceId) =>
oauthSources.sourcesOauthRetrieve({ slug: instanceId }),
oauthSources.sourcesOauthList({ pbmUuid: instanceId }),
),
);
return mappings
.filter((s) => s.status === "fulfilled")
.map((s) => s.value)
.filter((s) => s.pagination.count > 0)
.map((s) => s.results[0])
.map(sourceToSelect);
};
}

View File

@ -48,6 +48,7 @@ function renderHttpBasic(provider: Partial<ProxyProvider>) {
help=${msg(
"User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used.",
)}
inputHint="code"
>
</ak-text-input>
@ -56,6 +57,7 @@ function renderHttpBasic(provider: Partial<ProxyProvider>) {
label=${msg("HTTP-Basic Password Key")}
value="${ifDefined(provider?.basicAuthPasswordAttribute)}"
help=${msg("User/Group Attribute used for the password part of the HTTP-Basic Header.")}
inputHint="code"
>
</ak-text-input>`;
}
@ -88,6 +90,7 @@ function renderProxySettings(provider: Partial<ProxyProvider>, errors?: Validati
help=${msg(
"The external URL you'll access the application at. Include any non-standard port.",
)}
inputHint="code"
></ak-text-input>
<ak-text-input
name="internalHost"
@ -96,6 +99,7 @@ function renderProxySettings(provider: Partial<ProxyProvider>, errors?: Validati
required
.errorMessages=${errors?.internalHost ?? []}
help=${msg("Upstream host that the requests are forwarded to.")}
inputHint="code"
></ak-text-input>
<ak-switch-input
@ -122,6 +126,7 @@ function renderForwardSingleSettings(provider: Partial<ProxyProvider>, errors?:
help=${msg(
"The external URL you'll access the application at. Include any non-standard port.",
)}
inputHint="code"
></ak-text-input>`;
}
@ -220,6 +225,7 @@ export function renderForm(
.errorMessages=${errors?.accessTokenValidity ?? []}
required
.help=${msg("Configure how long tokens are valid for.")}
inputHint="code"
></ak-text-input>
<ak-form-group>
@ -251,7 +257,9 @@ export function renderForm(
: msg("Unauthenticated Paths")}"
name="skipPathRegex"
>
<textarea class="pf-c-form-control">${provider?.skipPathRegex}</textarea>
<textarea class="pf-c-form-control pf-m-monospace">
${provider?.skipPathRegex}</textarea
>
<p class="pf-c-form__helper-text">
${msg(
"Regular expressions for which authentication is not required. Each new line is interpreted as a new expression.",

View File

@ -13,7 +13,7 @@ import MDNginxStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy
import MDTraefikCompose from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_compose.md";
import MDTraefikIngress from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_ingress.md";
import MDTraefikStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_standalone.md";
import MDHeaderAuthentication from "@goauthentik/docs/add-secure-apps/providers/proxy/header_authentication.md";
import MDHeaderAuthentication from "@goauthentik/docs/add-secure-apps/providers/proxy/header_authentication.mdx";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/Markdown";
@ -118,7 +118,7 @@ export class ProxyProviderViewPage extends AKElement {
}
renderConfig(): TemplateResult {
const serves = [
const servers = [
{
label: msg("Nginx (Ingress)"),
md: MDNginxIngress,
@ -184,7 +184,7 @@ export class ProxyProviderViewPage extends AKElement {
},
];
return html`<ak-tabs pageIdentifier="proxy-setup">
${serves.map((server) => {
${servers.map((server) => {
return html`<section
slot="page-${convertToSlug(server.label)}"
data-tab-title="${server.label}"
@ -229,7 +229,7 @@ export class ProxyProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersProxyProxyprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersProxyProxyprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -102,7 +102,7 @@ export class EndpointListPage extends Table<Endpoint> {
</button>
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersRacEndpoint}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersRacEndpoint}
objectPk=${item.pk}
>
</ak-rbac-object-permission-modal>`,

View File

@ -83,7 +83,9 @@ export class RACProviderFormPage extends ModelForm<RACProvider, number> {
<input
type="text"
value="${first(this.instance?.connectionExpiry, "hours=8")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">

View File

@ -119,7 +119,7 @@ export class RACProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersRacRacprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersRacRacprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -83,6 +83,7 @@ export function renderForm(
randomString(128, ascii_letters + digits),
)}
required
inputHint="code"
></ak-text-input>
<ak-text-input
name="clientNetworks"
@ -91,6 +92,7 @@ export function renderForm(
.errorMessages=${errors?.clientNetworks ?? []}
required
help=${clientNetworksHelp}
inputHint="code"
></ak-text-input>
<ak-form-element-horizontal
label=${msg("Property mappings")}

View File

@ -169,7 +169,7 @@ export class RadiusProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersRadiusRadiusprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersRadiusRadiusprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -18,20 +18,20 @@ export const spBindingOptions = toOptions([
]);
export const digestAlgorithmOptions = toOptions([
["SHA1", DigestAlgorithmEnum._200009Xmldsigsha1],
["SHA256", DigestAlgorithmEnum._200104Xmlencsha256, true],
["SHA384", DigestAlgorithmEnum._200104XmldsigMoresha384],
["SHA512", DigestAlgorithmEnum._200104Xmlencsha512],
["SHA1", DigestAlgorithmEnum.HttpWwwW3Org200009Xmldsigsha1],
["SHA256", DigestAlgorithmEnum.HttpWwwW3Org200104Xmlencsha256, true],
["SHA384", DigestAlgorithmEnum.HttpWwwW3Org200104XmldsigMoresha384],
["SHA512", DigestAlgorithmEnum.HttpWwwW3Org200104Xmlencsha512],
]);
export const signatureAlgorithmOptions = toOptions([
["RSA-SHA1", SignatureAlgorithmEnum._200009XmldsigrsaSha1],
["RSA-SHA256", SignatureAlgorithmEnum._200104XmldsigMorersaSha256, true],
["RSA-SHA384", SignatureAlgorithmEnum._200104XmldsigMorersaSha384],
["RSA-SHA512", SignatureAlgorithmEnum._200104XmldsigMorersaSha512],
["ECDSA-SHA1", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha1],
["ECDSA-SHA256", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha256],
["ECDSA-SHA384", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha384],
["ECDSA-SHA512", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha512],
["DSA-SHA1", SignatureAlgorithmEnum._200009XmldsigdsaSha1],
["RSA-SHA1", SignatureAlgorithmEnum.HttpWwwW3Org200009XmldsigrsaSha1],
["RSA-SHA256", SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMorersaSha256, true],
["RSA-SHA384", SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMorersaSha384],
["RSA-SHA512", SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMorersaSha512],
["ECDSA-SHA1", SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMoreecdsaSha1],
["ECDSA-SHA256", SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMoreecdsaSha256],
["ECDSA-SHA384", SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMoreecdsaSha384],
["ECDSA-SHA512", SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMoreecdsaSha512],
["DSA-SHA1", SignatureAlgorithmEnum.HttpWwwW3Org200009XmldsigdsaSha1],
]);

View File

@ -247,7 +247,7 @@ export class SAMLProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersSamlSamlprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersSamlSamlprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -40,6 +40,7 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
.errorMessages=${errors?.url ?? []}
required
help=${msg("SCIM base url, usually ends in /v2.")}
inputHint="code"
></ak-text-input>
<ak-switch-input
@ -58,6 +59,7 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
help=${msg(
"Token to authenticate with. Currently only bearer authentication is supported.",
)}
inputHint="code"
></ak-text-input>
</div>
</ak-form-group>

View File

@ -33,7 +33,7 @@ export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
<span slot="header">${msg("Sync Group")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.Group}
model=${SyncObjectModelEnum.AuthentikCoreModelsGroup}
.sync=${(data: ProvidersScimSyncObjectCreateRequest) => {
return new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate(data);
}}

View File

@ -33,7 +33,7 @@ export class SCIMProviderUserList extends Table<SCIMProviderUser> {
<span slot="header">${msg("Sync User")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.User}
model=${SyncObjectModelEnum.AuthentikCoreModelsUser}
.sync=${(data: ProvidersScimSyncObjectCreateRequest) => {
return new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate(data);
}}

View File

@ -130,7 +130,7 @@ export class SCIMProviderViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.ProvidersScimScimprovider}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersScimScimprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -0,0 +1,122 @@
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import {
oauth2ProvidersProvider,
oauth2ProvidersSelector,
} from "@goauthentik/admin/providers/oauth2/OAuth2ProvidersProvider";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/SearchSelect";
import "@goauthentik/elements/utils/TimeDeltaHelp";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { ProvidersApi, SSFProvider } from "@goauthentik/api";
/**
* Form page for SSF Authentication Method
*
* @element ak-provider-ssf-form
*
*/
@customElement("ak-provider-ssf-form")
export class SSFProviderFormPage extends BaseProviderForm<SSFProvider> {
async loadInstance(pk: number): Promise<SSFProvider> {
const provider = await new ProvidersApi(DEFAULT_CONFIG).providersSsfRetrieve({
id: pk,
});
return provider;
}
async send(data: SSFProvider): Promise<SSFProvider> {
if (this.instance) {
return new ProvidersApi(DEFAULT_CONFIG).providersSsfUpdate({
id: this.instance.pk,
sSFProviderRequest: data,
});
} else {
return new ProvidersApi(DEFAULT_CONFIG).providersSsfCreate({
sSFProviderRequest: data,
});
}
}
renderForm(): TemplateResult {
const provider = this.instance;
return html`<ak-text-input
name="name"
label=${msg("Name")}
value=${ifDefined(provider?.name)}
required
></ak-text-input>
<ak-form-group expanded>
<span slot="header"> ${msg("Protocol settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal label=${msg("Signing Key")} name="signingKey">
<!-- NOTE: 'null' cast to 'undefined' on signingKey to satisfy Lit requirements -->
<ak-crypto-certificate-search
certificate=${ifDefined(provider?.signingKey ?? undefined)}
singleton
></ak-crypto-certificate-search>
<p class="pf-c-form__helper-text">${msg("Key used to sign the events.")}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Event Retention")}
required
name="eventRetention"
>
<input
type="text"
value="${first(provider?.eventRetention, "days=30")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"Determines how long events are stored for. If an event could not be sent correctly, its expiration is also increased by this duration.",
)}
</p>
<ak-utils-time-delta-help></ak-utils-time-delta-help>
</ak-form-element-horizontal>
</div>
</ak-form-group>
<ak-form-group>
<span slot="header">${msg("Authentication settings")}</span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("OIDC Providers")}
name="oidcAuthProviders"
>
<ak-dual-select-dynamic-selected
.provider=${oauth2ProvidersProvider}
.selector=${oauth2ProvidersSelector(provider?.oidcAuthProviders)}
available-label=${msg("Available Providers")}
selected-label=${msg("Selected Providers")}
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"JWTs signed by the selected providers can be used to authenticate to this provider.",
)}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-provider-ssf-form": SSFProviderFormPage;
}
}

View File

@ -0,0 +1,175 @@
import "@goauthentik/admin/providers/RelatedApplicationButton";
import "@goauthentik/admin/providers/ssf/SSFProviderFormPage";
import "@goauthentik/admin/providers/ssf/StreamTable";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import "@goauthentik/components/events/ObjectChangelog";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import {
ProvidersApi,
RbacPermissionsAssignedByUsersListModelEnum,
SSFProvider,
} from "@goauthentik/api";
@customElement("ak-provider-ssf-view")
export class SSFProviderViewPage extends AKElement {
@property({ type: Number })
set providerID(value: number) {
new ProvidersApi(DEFAULT_CONFIG)
.providersSsfRetrieve({
id: value,
})
.then((prov) => {
this.provider = prov;
});
}
@property({ attribute: false })
provider?: SSFProvider;
static get styles(): CSSResult[] {
return [
PFBase,
PFButton,
PFPage,
PFGrid,
PFContent,
PFCard,
PFDescriptionList,
PFForm,
PFFormControl,
PFBanner,
PFDivider,
];
}
constructor() {
super();
this.addEventListener(EVENT_REFRESH, () => {
if (!this.provider?.pk) return;
this.providerID = this.provider?.pk;
});
}
render(): TemplateResult {
if (!this.provider) {
return html``;
}
return html` <ak-tabs>
<section slot="page-overview" data-tab-title="${msg("Overview")}">
${this.renderTabOverview()}
</section>
<section
slot="page-changelog"
data-tab-title="${msg("Changelog")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-object-changelog
targetModelPk=${this.provider?.pk || ""}
targetModelName=${this.provider?.metaModelName || ""}
>
</ak-object-changelog>
</div>
</div>
</section>
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikProvidersSsfSsfprovider}
objectPk=${this.provider.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;
}
renderTabOverview(): TemplateResult {
if (!this.provider) {
return html``;
}
return html`<div slot="header" class="pf-c-banner pf-m-info">
${msg("SSF Provider is in preview.")}
<a href="mailto:hello+feature/ssf@goauthentik.io">${msg("Send us feedback!")}</a>
</div>
<div class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
<div
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl"
>
<div class="pf-c-card__body">
<dl class="pf-c-description-list">
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${msg("Name")}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
${this.provider.name}
</div>
</dd>
</div>
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${msg("URL")}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<input
class="pf-c-form-control"
readonly
type="text"
value=${this.provider.ssfUrl || ""}
/>
</div>
</dd>
</div>
</dl>
</div>
<div class="pf-c-card__footer">
<ak-forms-modal>
<span slot="submit"> ${msg("Update")} </span>
<span slot="header"> ${msg("Update SSF Provider")} </span>
<ak-provider-ssf-form slot="form" .instancePk=${this.provider.pk || 0}>
</ak-provider-ssf-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${msg("Edit")}
</button>
</ak-forms-modal>
</div>
</div>
<div class="pf-c-card pf-l-grid__item pf-m-8-col-on-2xl">
<div class="pf-c-card__title">${msg("Streams")}</div>
<ak-provider-ssf-stream-list .providerId=${this.providerID}>
</ak-provider-ssf-stream-list>
</div>
</div>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-provider-ssf-view": SSFProviderViewPage;
}
}

View File

@ -0,0 +1,50 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm";
import "@goauthentik/elements/forms/ProxyForm";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { SSFStream, SsfApi } from "@goauthentik/api";
@customElement("ak-provider-ssf-stream-list")
export class SSFProviderStreamList extends Table<SSFStream> {
searchEnabled(): boolean {
return true;
}
checkbox = true;
clearOnRefresh = true;
@property({ type: Number })
providerId?: number;
@property()
order = "name";
async apiEndpoint(): Promise<PaginatedResponse<SSFStream>> {
return new SsfApi(DEFAULT_CONFIG).ssfStreamsList({
provider: this.providerId,
...(await this.defaultEndpointConfig()),
});
}
columns(): TableColumn[] {
return [new TableColumn(msg("Audience"), "aud")];
}
row(item: SSFStream): TemplateResult[] {
return [html`${item.aud}`];
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-provider-ssf-stream-list": SSFProviderStreamList;
}
}

View File

@ -35,10 +35,10 @@ export class ObjectPermissionPage extends AKElement {
render() {
return html` <ak-tabs pageIdentifier="permissionPage" ?vertical=${!this.embedded}>
${this.model === RbacPermissionsAssignedByUsersListModelEnum.CoreUser
${this.model === RbacPermissionsAssignedByUsersListModelEnum.AuthentikCoreUser
? this.renderCoreUser()
: nothing}
${this.model === RbacPermissionsAssignedByUsersListModelEnum.RbacRole
${this.model === RbacPermissionsAssignedByUsersListModelEnum.AuthentikRbacRole
? this.renderRbacRole()
: nothing}
<section

View File

@ -35,7 +35,7 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
const perms = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesList({
...(await this.defaultEndpointConfig()),
// TODO: better default
model: this.model || RbacPermissionsAssignedByRolesListModelEnum.CoreUser,
model: this.model || RbacPermissionsAssignedByRolesListModelEnum.AuthentikCoreUser,
objectPk: this.objectPk?.toString(),
});
const [appLabel, modelName] = (this.model || "").split(".");

View File

@ -35,7 +35,7 @@ export class UserAssignedObjectPermissionTable extends Table<UserAssignedObjectP
const perms = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersList({
...(await this.defaultEndpointConfig()),
// TODO: better default
model: this.model || RbacPermissionsAssignedByUsersListModelEnum.CoreUser,
model: this.model || RbacPermissionsAssignedByUsersListModelEnum.AuthentikCoreUser,
objectPk: this.objectPk?.toString(),
});
const [appLabel, modelName] = (this.model || "").split(".");

View File

@ -121,7 +121,7 @@ export class RoleViewPage extends AKElement {
<div class="pf-c-card__title">${msg("Changelog")}</div>
<div class="pf-c-card__body">
<ak-object-changelog
targetModelPk=${this.roleId}
targetModelPk=${this._role.pk}
targetModelApp="authentik_rbac"
targetModelName="role"
>
@ -133,7 +133,7 @@ export class RoleViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.RbacRole}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikRbacRole}
objectPk=${this._role.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -216,7 +216,7 @@ export class KerberosSourceViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.SourcesKerberosKerberossource}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikSourcesKerberosKerberossource}
objectPk=${this.source.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -192,7 +192,7 @@ export class LDAPSourceViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.SourcesLdapLdapsource}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikSourcesLdapLdapsource}
objectPk=${this.source.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -126,7 +126,9 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
this.providerType.authorizationUrl,
"",
)}"
class="pf-c-form-control"
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.")}
@ -134,13 +136,15 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Access token URL")} name="accessTokenUrl">
<input
type="text"
type="url"
value="${first(
this.instance?.accessTokenUrl,
this.providerType.accessTokenUrl,
"",
)}"
class="pf-c-form-control"
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.")}
@ -148,13 +152,15 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Profile URL")} name="profileUrl">
<input
type="text"
type="url"
value="${first(
this.instance?.profileUrl,
this.providerType.profileUrl,
"",
)}"
class="pf-c-form-control"
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.")}
@ -166,9 +172,10 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
name="requestTokenUrl"
>
<input
type="text"
type="url"
value="${first(this.instance?.requestTokenUrl, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
/>
<p class="pf-c-form__helper-text">
${msg(
@ -184,13 +191,15 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
name="oidcWellKnownUrl"
>
<input
type="text"
type="url"
value="${first(
this.instance?.oidcWellKnownUrl,
this.providerType.oidcWellKnownUrl,
"",
)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
<p class="pf-c-form__helper-text">
${msg(
@ -206,13 +215,15 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
name="oidcJwksUrl"
>
<input
type="text"
type="url"
value="${first(
this.instance?.oidcJwksUrl,
this.providerType.oidcJwksUrl,
"",
)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
<p class="pf-c-form__helper-text">
${msg(
@ -246,7 +257,9 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
<input
type="text"
value="${ifDefined(this.instance?.slug)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
</ak-form-element-horizontal>
@ -344,7 +357,9 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
this.instance?.userPathTemplate,
"goauthentik.io/sources/%(slug)s",
)}"
class="pf-c-form-control"
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>
@ -390,7 +405,9 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
<input
type="text"
value="${first(this.instance?.icon, "")}"
class="pf-c-form-control"
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>`}
@ -406,7 +423,9 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
<input
type="text"
value="${ifDefined(this.instance?.consumerKey)}"
class="pf-c-form-control"
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>
@ -417,14 +436,20 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
?writeOnly=${this.instance !== undefined}
name="consumerSecret"
>
<textarea class="pf-c-form-control"></textarea>
<textarea
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
></textarea>
<p class="pf-c-form__helper-text">${msg("Also known as Client Secret.")}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Scopes")} name="additionalScopes">
<input
type="text"
value="${first(this.instance?.additionalScopes, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
<p class="pf-c-form__helper-text">
${msg(

View File

@ -253,7 +253,7 @@ export class OAuthSourceViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.SourcesOauthOauthsource}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikSourcesOauthOauthsource}
objectPk=${this.source.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -143,7 +143,7 @@ export class PlexSourceViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.SourcesPlexPlexsource}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikSourcesPlexPlexsource}
objectPk=${this.source.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -358,37 +358,37 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
>
<select class="pf-c-form-control">
<option
value=${NameIdPolicyEnum._20nameidFormatpersistent}
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum._20nameidFormatpersistent}
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
>
${msg("Persistent")}
</option>
<option
value=${NameIdPolicyEnum._11nameidFormatemailAddress}
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum._11nameidFormatemailAddress}
NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
>
${msg("Email address")}
</option>
<option
value=${NameIdPolicyEnum._20nameidFormatWindowsDomainQualifiedName}
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum._20nameidFormatWindowsDomainQualifiedName}
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
>
${msg("Windows")}
</option>
<option
value=${NameIdPolicyEnum._11nameidFormatX509SubjectName}
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum._11nameidFormatX509SubjectName}
NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
>
${msg("X509 Subject")}
</option>
<option
value=${NameIdPolicyEnum._20nameidFormattransient}
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
?selected=${this.instance?.nameIdPolicy ===
NameIdPolicyEnum._20nameidFormattransient}
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
>
${msg("Transient")}
</option>
@ -432,20 +432,20 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
.options=${[
{
label: "SHA1",
value: DigestAlgorithmEnum._200009Xmldsigsha1,
value: DigestAlgorithmEnum.HttpWwwW3Org200009Xmldsigsha1,
},
{
label: "SHA256",
value: DigestAlgorithmEnum._200104Xmlencsha256,
value: DigestAlgorithmEnum.HttpWwwW3Org200104Xmlencsha256,
default: true,
},
{
label: "SHA384",
value: DigestAlgorithmEnum._200104XmldsigMoresha384,
value: DigestAlgorithmEnum.HttpWwwW3Org200104XmldsigMoresha384,
},
{
label: "SHA512",
value: DigestAlgorithmEnum._200104Xmlencsha512,
value: DigestAlgorithmEnum.HttpWwwW3Org200104Xmlencsha512,
},
]}
.value=${this.instance?.digestAlgorithm}
@ -461,24 +461,24 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
.options=${[
{
label: "RSA-SHA1",
value: SignatureAlgorithmEnum._200009XmldsigrsaSha1,
value: SignatureAlgorithmEnum.HttpWwwW3Org200009XmldsigrsaSha1,
},
{
label: "RSA-SHA256",
value: SignatureAlgorithmEnum._200104XmldsigMorersaSha256,
value: SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMorersaSha256,
default: true,
},
{
label: "RSA-SHA384",
value: SignatureAlgorithmEnum._200104XmldsigMorersaSha384,
value: SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMorersaSha384,
},
{
label: "RSA-SHA512",
value: SignatureAlgorithmEnum._200104XmldsigMorersaSha512,
value: SignatureAlgorithmEnum.HttpWwwW3Org200104XmldsigMorersaSha512,
},
{
label: "DSA-SHA1",
value: SignatureAlgorithmEnum._200009XmldsigdsaSha1,
value: SignatureAlgorithmEnum.HttpWwwW3Org200009XmldsigdsaSha1,
},
]}
.value=${this.instance?.signatureAlgorithm}

View File

@ -220,7 +220,7 @@ export class SAMLSourceViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.SourcesSamlSamlsource}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikSourcesSamlSamlsource}
objectPk=${this.source.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -16,7 +16,6 @@ import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
@ -60,7 +59,6 @@ export class SCIMSourceViewPage extends AKElement {
PFContent,
PFCard,
PFDescriptionList,
PFBanner,
];
}
@ -78,12 +76,6 @@ export class SCIMSourceViewPage extends AKElement {
}
return html`<ak-tabs>
<section slot="page-overview" data-tab-title="${msg("Overview")}">
<div slot="header" class="pf-c-banner pf-m-info">
${msg("SCIM Source is in preview.")}
<a href="mailto:hello+feature/scim-source@goauthentik.io"
>${msg("Send us feedback!")}</a
>
</div>
<div class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
<div class="pf-c-card pf-l-grid__item pf-m-12-col">
<div class="pf-c-card__body">
@ -207,7 +199,7 @@ export class SCIMSourceViewPage extends AKElement {
<ak-rbac-object-permission-page
slot="page-permissions"
data-tab-title="${msg("Permissions")}"
model=${RbacPermissionsAssignedByUsersListModelEnum.SourcesScimScimsource}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikSourcesScimScimsource}
objectPk=${this.source.pk}
></ak-rbac-object-permission-page>
</ak-tabs>`;

View File

@ -2,6 +2,7 @@ import "@goauthentik/admin/rbac/ObjectPermissionModal";
import "@goauthentik/admin/stages/StageWizard";
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
import "@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm";
import "@goauthentik/admin/stages/authenticator_email/AuthenticatorEmailStageForm";
import "@goauthentik/admin/stages/authenticator_endpoint_gdtc/AuthenticatorEndpointGDTCStageForm";
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";

View File

@ -1,6 +1,7 @@
import "@goauthentik/admin/common/ak-license-notice";
import { StageBindingForm } from "@goauthentik/admin/flows/StageBindingForm";
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
import "@goauthentik/admin/stages/authenticator_email/AuthenticatorEmailStageForm";
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";

View File

@ -79,7 +79,9 @@ export class AuthenticatorDuoStageForm extends BaseStageForm<AuthenticatorDuoSta
<input
type="text"
value="${first(this.instance?.apiHostname, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
</ak-form-element-horizontal>
@ -104,7 +106,14 @@ export class AuthenticatorDuoStageForm extends BaseStageForm<AuthenticatorDuoSta
?writeOnly=${this.instance !== undefined}
name="clientSecret"
>
<input type="text" value="" class="pf-c-form-control" required />
<input
type="text"
value=""
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
</ak-form-element-horizontal>
</div>
</ak-form-group>
@ -124,7 +133,9 @@ export class AuthenticatorDuoStageForm extends BaseStageForm<AuthenticatorDuoSta
<input
type="text"
value="${first(this.instance?.adminIntegrationKey, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
@ -132,7 +143,13 @@ export class AuthenticatorDuoStageForm extends BaseStageForm<AuthenticatorDuoSta
?writeOnly=${this.instance !== undefined}
name="adminSecretKey"
>
<input type="text" value="" class="pf-c-form-control" />
<input
type="text"
value=""
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
</ak-form-element-horizontal>
</div>
</ak-form-group>

View File

@ -0,0 +1,283 @@
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
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 { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import {
AuthenticatorEmailStage,
Flow,
FlowsApi,
FlowsInstancesListDesignationEnum,
FlowsInstancesListRequest,
StagesApi,
} from "@goauthentik/api";
@customElement("ak-stage-authenticator-email-form")
export class AuthenticatorEmailStageForm extends BaseStageForm<AuthenticatorEmailStage> {
async loadInstance(pk: string): Promise<AuthenticatorEmailStage> {
const stage = await new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEmailRetrieve({
stageUuid: pk,
});
this.showConnectionSettings = !stage.useGlobalSettings;
return stage;
}
@property({ type: Boolean })
showConnectionSettings = false;
async send(data: AuthenticatorEmailStage): Promise<AuthenticatorEmailStage> {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEmailUpdate({
stageUuid: this.instance.pk || "",
authenticatorEmailStageRequest: data,
});
} else {
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEmailCreate({
authenticatorEmailStageRequest: data,
});
}
}
renderConnectionSettings(): TemplateResult {
if (!this.showConnectionSettings) {
return html``;
}
return html`<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Connection settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal label=${msg("SMTP Host")} ?required=${true} name="host">
<input
type="text"
value="${ifDefined(this.instance?.host || "")}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("SMTP Port")} ?required=${true} name="port">
<input
type="number"
value="${first(this.instance?.port, 25)}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("SMTP Username")} name="username">
<input
type="text"
value="${ifDefined(this.instance?.username || "")}"
class="pf-c-form-control"
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("SMTP Password")}
?writeOnly=${this.instance !== undefined}
name="password"
>
<input type="text" value="" class="pf-c-form-control" />
</ak-form-element-horizontal>
<ak-form-element-horizontal name="useTls">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.useTls, 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("Use TLS")}</span>
</label>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="useSsl">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.useSsl, false)}
/>
<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("Use SSL")}</span>
</label>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Timeout")}
?required=${true}
name="timeout"
>
<input
type="number"
value="${first(this.instance?.timeout, 30)}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("From address")}
?required=${true}
name="fromAddress"
>
<input
type="text"
value="${ifDefined(this.instance?.fromAddress || "system@authentik.local")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg("Email address the verification email will be sent from.")}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>`;
}
renderForm(): TemplateResult {
return html` <span> ${msg("Stage used to configure an email-based authenticator.")} </span>
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
<input
type="text"
value="${first(this.instance?.name, "")}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Authenticator type name")}
?required=${false}
name="friendlyName"
>
<input
type="text"
value="${first(this.instance?.friendlyName, "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${msg(
"Display name of this authenticator, used by users when they enroll an authenticator.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="useGlobalSettings">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.useGlobalSettings, true)}
@change=${(ev: Event) => {
const target = ev.target as HTMLInputElement;
this.showConnectionSettings = !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("Use global connection settings")}</span>
</label>
<p class="pf-c-form__helper-text">
${msg(
"When enabled, global email connection settings will be used and connection settings below will be ignored.",
)}
</p>
</ak-form-element-horizontal>
${this.renderConnectionSettings()}
<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Stage-specific settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Subject")}
?required=${true}
name="subject"
>
<input
type="text"
value="${first(this.instance?.subject, "authentik Sign-in code")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg("Subject of the verification email.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Token expiration")}
?required=${true}
name="tokenExpiry"
>
<input
type="text"
value="${first(this.instance?.tokenExpiry, "minutes=15")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Configuration flow")}
name="configureFlow"
>
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Flow[]> => {
const args: FlowsInstancesListRequest = {
ordering: "slug",
designation:
FlowsInstancesListDesignationEnum.StageConfiguration,
};
if (query !== undefined) {
args.search = query;
}
const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(
args,
);
return flows.results;
}}
.renderElement=${(flow: Flow): string => {
return RenderFlowOption(flow);
}}
.renderDescription=${(flow: Flow): TemplateResult => {
return html`${flow.name}`;
}}
.value=${(flow: Flow | undefined): string | undefined => {
return flow?.pk;
}}
.selected=${(flow: Flow): boolean => {
return this.instance?.configureFlow === flow.pk;
}}
?blankable=${true}
>
</ak-search-select>
<p class="pf-c-form__helper-text">
${msg(
"Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.",
)}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-stage-authenticator-email-form": AuthenticatorEmailStageForm;
}
}

View File

@ -67,7 +67,9 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
<input
type="text"
value="${first(this.instance?.accountSid, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
@ -82,7 +84,9 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
<input
type="text"
value="${first(this.instance?.auth, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
@ -126,7 +130,9 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
<input
type="text"
value="${first(this.instance?.accountSid, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
@ -141,7 +147,9 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
<input
type="text"
value="${first(this.instance?.auth, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
<p class="pf-c-form__helper-text">
${msg(
@ -157,7 +165,9 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
<input
type="text"
value="${first(this.instance?.authPassword, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
/>
<p class="pf-c-form__helper-text">
${msg("This is the password to be used with basic auth")}
@ -263,7 +273,9 @@ export class AuthenticatorSMSStageForm extends BaseStageForm<AuthenticatorSMSSta
<input
type="text"
value="${first(this.instance?.fromNumber, "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">

View File

@ -79,6 +79,7 @@ export class AuthenticatorValidateStageForm extends BaseStageForm<AuthenticatorV
[DeviceClassesEnum.Webauthn, msg("WebAuthn Authenticators")],
[DeviceClassesEnum.Duo, msg("Duo Authenticators")],
[DeviceClassesEnum.Sms, msg("SMS-based Authenticators")],
[DeviceClassesEnum.Email, msg("Email-based Authenticators")],
];
return html`
@ -125,7 +126,9 @@ export class AuthenticatorValidateStageForm extends BaseStageForm<AuthenticatorV
<input
type="text"
value="${this.instance?.lastAuthThreshold || "seconds=0"}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">

View File

@ -59,7 +59,9 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
<input
type="text"
value="${ifDefined(this.instance?.publicKey || "")}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
@ -74,7 +76,14 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
?writeOnly=${this.instance !== undefined}
name="privateKey"
>
<input type="text" value="" class="pf-c-form-control" required />
<input
type="text"
value=""
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"Private key, acquired from https://www.google.com/recaptcha/intro/v3.html.",
@ -135,12 +144,14 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
name="jsUrl"
>
<input
type="text"
type="url"
value="${ifDefined(
this.instance?.jsUrl ||
"https://www.recaptcha.net/recaptcha/api.js",
)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">
@ -155,12 +166,14 @@ export class CaptchaStageForm extends BaseStageForm<CaptchaStage> {
name="apiUrl"
>
<input
type="text"
type="url"
value="${ifDefined(
this.instance?.apiUrl ||
"https://www.recaptcha.net/recaptcha/api/siteverify",
)}"
class="pf-c-form-control"
class="pf-c-form-control pf-m-monospace"
autocomplete="off"
spellcheck="false"
required
/>
<p class="pf-c-form__helper-text">

View File

@ -27,13 +27,13 @@ export function sourcesSelector(instanceSources: string[] | undefined) {
return async () => {
const sourcesApi = new SourcesApi(DEFAULT_CONFIG);
const sources = await Promise.allSettled(
instanceSources.map((instanceId) =>
sourcesApi.sourcesAllRetrieve({ slug: instanceId }),
),
instanceSources.map((instanceId) => sourcesApi.sourcesAllList({ pbmUuid: instanceId })),
);
return sources
.filter((s) => s.status === "fulfilled")
.map((s) => s.value)
.filter((s) => s.pagination.count > 0)
.map((s) => s.results[0])
.map(sourceToSelect);
};
}

View File

@ -141,7 +141,7 @@ export class InvitationListPage extends TablePage<Invitation> {
</button>
</ak-forms-modal>
<ak-rbac-object-permission-modal
model=${RbacPermissionsAssignedByUsersListModelEnum.StagesInvitationInvitation}
model=${RbacPermissionsAssignedByUsersListModelEnum.AuthentikStagesInvitationInvitation}
objectPk=${item.pk}
>
</ak-rbac-object-permission-modal>`,

View File

@ -55,19 +55,19 @@ export class PasswordStageForm extends BaseStageForm<PasswordStage> {
renderForm(): TemplateResult {
const backends = [
{
name: BackendsEnum.CoreAuthInbuiltBackend,
name: BackendsEnum.AuthentikCoreAuthInbuiltBackend,
label: msg("User database + standard password"),
},
{
name: BackendsEnum.CoreAuthTokenBackend,
name: BackendsEnum.AuthentikCoreAuthTokenBackend,
label: msg("User database + app passwords"),
},
{
name: BackendsEnum.SourcesLdapAuthLdapBackend,
name: BackendsEnum.AuthentikSourcesLdapAuthLdapBackend,
label: msg("User database + LDAP password"),
},
{
name: BackendsEnum.SourcesKerberosAuthKerberosBackend,
name: BackendsEnum.AuthentikSourcesKerberosAuthKerberosBackend,
label: msg("User database + Kerberos password"),
},
];

Some files were not shown because too many files have changed in this diff Show More