diff --git a/web/.eslintrc.precommit.json b/web/.eslintrc.precommit.json new file mode 100644 index 0000000000..6e81348788 --- /dev/null +++ b/web/.eslintrc.precommit.json @@ -0,0 +1,30 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:lit/recommended", + "plugin:custom-elements/recommended", + "plugin:storybook/recommended", + "plugin:sonarjs/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint", "lit", "custom-elements", "sonarjs"], + "rules": { + "indent": "off", + "linebreak-style": ["error", "unix"], + "quotes": ["error", "double", { "avoidEscape": true }], + "semi": ["error", "always"], + "@typescript-eslint/ban-ts-comment": "off", + "sonarjs/cognitive-complexity": ["warn", 9], + "sonarjs/no-duplicate-string": "off", + "sonarjs/no-nested-template-literals": "off" + } +} diff --git a/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts index b87d43fd5a..df6116ecb1 100644 --- a/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts +++ b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts @@ -1,16 +1,17 @@ import "@goauthentik/admin/applications/wizard/ak-wizard-title"; import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search"; +import { + makeOAuth2PropertyMappingsSelector, + oauth2PropertyMappingsProvider, +} from "@goauthentik/admin/providers/oauth2/OAuth2PropertyMappings.js"; import { clientTypeOptions, issuerModeOptions, redirectUriHelp, subjectModeOptions, } from "@goauthentik/admin/providers/oauth2/OAuth2ProviderForm"; -import { - makeOAuth2PropertyMappingsSelector, - oauth2PropertyMappingsProvider, -} from "@goauthentik/admin/providers/oauth2/Oauth2PropertyMappings.js"; +import { oauth2SourcesProvider } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils"; import "@goauthentik/components/ak-number-input"; @@ -262,24 +263,17 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel { name="jwksSources" .errorMessages=${errors?.jwksSources ?? []} > - +

${msg( "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", )}

-

- ${msg("Hold control/command to select multiple items.")} -

diff --git a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts index 05d45f6c86..36058f9d7d 100644 --- a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts +++ b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts @@ -1,4 +1,5 @@ import "@goauthentik/admin/applications/wizard/ak-wizard-title"; +import { oauth2SourcesProvider } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js"; import { makeProxyPropertyMappingsSelector, proxyPropertyMappingsProvider, @@ -10,6 +11,7 @@ import "@goauthentik/components/ak-text-input"; import "@goauthentik/components/ak-textarea-input"; import "@goauthentik/components/ak-toggle-group"; 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/HorizontalFormElement"; import { msg } from "@lit/localize"; @@ -226,26 +228,17 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel { name="jwksSources" .errorMessages=${errors?.jwksSources ?? []} > - +

${msg( "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", )}

-

- ${msg("Hold control/command to select multiple items.")} -

diff --git a/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts index f2947a0381..403398bf37 100644 --- a/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts +++ b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts @@ -266,11 +266,8 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane .options=${propertyPairs} .values=${pmValues} .richhelp=${html`

- ${msg("Property mappings used for user mapping.")} -

-

- ${msg("Hold control/command to select multiple items.")} -

`} + ${msg("Property mappings used for user mapping.")} +

`} > + .richhelp=${html` +

${msg("Property mappings used for user mapping.")}

-

- ${msg("Hold control/command to select multiple items.")} -

`} + `} > + .richhelp=${html` +

${msg("Property mappings used for group creation.")}

-

- ${msg("Hold control/command to select multiple items.")} -

`} + `} >
diff --git a/web/src/admin/events/RuleForm.ts b/web/src/admin/events/RuleForm.ts index 7e2ff57b5d..b26318adb1 100644 --- a/web/src/admin/events/RuleForm.ts +++ b/web/src/admin/events/RuleForm.ts @@ -20,6 +20,20 @@ import { SeverityEnum, } from "@goauthentik/api"; +async function eventTransportsProvider(page = 1, search = "") { + const eventTransports = await new EventsApi(DEFAULT_CONFIG).eventsTransportsList({ + ordering: "name", + pageSize: 20, + search: search.trim(), + page, + }); + + return { + pagination: eventTransports.pagination, + options: eventTransports.results.map((transport) => [transport.pk, transport.name]), + }; +} + @customElement("ak-event-rule-form") export class RuleForm extends ModelForm { eventTransports?: PaginatedNotificationTransportList; @@ -100,24 +114,17 @@ export class RuleForm extends ModelForm { ?required=${true} name="transports" > - +

${msg( "Select which transports should be used to notify the user. If none are selected, the notification will only be shown in the authentik UI.", )}

-

- ${msg("Hold control/command to select multiple items.")} -

{ html`${item.name}`, html`${item.parentName || msg("-")}`, html`${Array.from(item.users || []).length}`, - html``, + html``, html` ${msg("Update")} ${msg("Update Group")} diff --git a/web/src/admin/providers/ProviderListPage.ts b/web/src/admin/providers/ProviderListPage.ts index ce96d63faf..e6922b5ccb 100644 --- a/web/src/admin/providers/ProviderListPage.ts +++ b/web/src/admin/providers/ProviderListPage.ts @@ -2,7 +2,7 @@ import "@goauthentik/admin/applications/ApplicationWizardHint"; import "@goauthentik/admin/providers/ProviderWizard"; import "@goauthentik/admin/providers/google_workspace/GoogleWorkspaceProviderForm"; import "@goauthentik/admin/providers/ldap/LDAPProviderForm"; -import "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderViewPage"; +import "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderForm"; import "@goauthentik/admin/providers/oauth2/OAuth2ProviderForm"; import "@goauthentik/admin/providers/proxy/ProxyProviderForm"; import "@goauthentik/admin/providers/rac/RACProviderForm"; diff --git a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts index b99fb4bebf..86f2598db0 100644 --- a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts +++ b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts @@ -1,8 +1,14 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm"; +import { + googleWorkspacePropertyMappingsProvider, + makeGoogleWorkspacePropertyMappingsSelector, +} from "@goauthentik/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; import "@goauthentik/elements/CodeMirror"; import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror"; +import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; +import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/Radio"; @@ -19,8 +25,6 @@ import { GoogleWorkspaceProvider, Group, OutgoingSyncDeleteAction, - PaginatedGoogleWorkspaceProviderMappingList, - PropertymappingsApi, ProvidersApi, } from "@goauthentik/api"; @@ -32,16 +36,6 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm { - this.propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderGoogleWorkspaceList({ - ordering: "managed", - }); - } - - propertyMappings?: PaginatedGoogleWorkspaceProviderMappingList; - async send(data: GoogleWorkspaceProvider): Promise { if (this.instance) { return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceUpdate({ @@ -229,68 +223,35 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm - +

${msg("Property mappings used to user mapping.")}

-

- ${msg("Hold control/command to select multiple items.")} -

- +

${msg("Property mappings used to group creation.")}

-

- ${msg("Hold control/command to select multiple items.")} -

`; diff --git a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings.ts b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings.ts new file mode 100644 index 0000000000..36996885b9 --- /dev/null +++ b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings.ts @@ -0,0 +1,30 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { PropertymappingsApi, ScopeMapping } from "@goauthentik/api"; + +export async function googleWorkspacePropertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderGoogleWorkspaceList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map((scope) => [scope.pk, scope.name, scope.name, scope]), + }; +} + +export function makeGoogleWorkspacePropertyMappingsSelector( + instanceMappings: string[] | undefined, + defaultSelection: string, +) { + const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; + return localMappings + ? ([pk, _]: DualSelectPair) => localMappings.has(pk) + : ([_0, _1, _2, scope]: DualSelectPair) => + scope?.managed === defaultSelection; +} diff --git a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderFormPage.ts b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderForm.ts similarity index 76% rename from web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderFormPage.ts rename to web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderForm.ts index d6c62de86d..de2eb396eb 100644 --- a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderFormPage.ts +++ b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderForm.ts @@ -1,6 +1,12 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm"; +import { + makeMicrosoftEntraPropertyMappingsSelector, + microsoftEntraPropertyMappingsProvider, +} from "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderPropertyMappings"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; +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/Radio"; @@ -17,8 +23,6 @@ import { Group, MicrosoftEntraProvider, OutgoingSyncDeleteAction, - PaginatedMicrosoftEntraProviderMappingList, - PropertymappingsApi, ProvidersApi, } from "@goauthentik/api"; @@ -30,16 +34,6 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm { - this.propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderMicrosoftEntraList({ - ordering: "managed", - }); - } - - propertyMappings?: PaginatedMicrosoftEntraProviderMappingList; - async send(data: MicrosoftEntraProvider): Promise { if (this.instance) { return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraUpdate({ @@ -218,68 +212,35 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm - +

${msg("Property mappings used to user mapping.")}

-

- ${msg("Hold control/command to select multiple items.")} -

- +

${msg("Property mappings used to group creation.")}

-

- ${msg("Hold control/command to select multiple items.")} -

`; diff --git a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderPropertyMappings.ts b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderPropertyMappings.ts new file mode 100644 index 0000000000..6a9fa34dd5 --- /dev/null +++ b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderPropertyMappings.ts @@ -0,0 +1,30 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { PropertymappingsApi, ScopeMapping } from "@goauthentik/api"; + +export async function microsoftEntraPropertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderMicrosoftEntraList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map((scope) => [scope.pk, scope.name, scope.name, scope]), + }; +} + +export function makeMicrosoftEntraPropertyMappingsSelector( + instanceMappings: string[] | undefined, + defaultSelection: string, +) { + const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; + return localMappings + ? ([pk, _]: DualSelectPair) => localMappings.has(pk) + : ([_0, _1, _2, scope]: DualSelectPair) => + scope?.managed === defaultSelection; +} diff --git a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderViewPage.ts b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderViewPage.ts index 4cb7f34d59..bf4197c936 100644 --- a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderViewPage.ts +++ b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderViewPage.ts @@ -1,4 +1,4 @@ -import "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderFormPage"; +import "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderForm"; import "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderGroupList"; import "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderUserList"; import "@goauthentik/admin/rbac/ObjectPermissionsPage"; diff --git a/web/src/admin/providers/oauth2/Oauth2PropertyMappings.ts b/web/src/admin/providers/oauth2/OAuth2PropertyMappings.ts similarity index 100% rename from web/src/admin/providers/oauth2/Oauth2PropertyMappings.ts rename to web/src/admin/providers/oauth2/OAuth2PropertyMappings.ts diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts index 935b976a36..5cb3c417b5 100644 --- a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts +++ b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts @@ -7,6 +7,7 @@ import "@goauthentik/components/ak-radio-input"; import "@goauthentik/components/ak-text-input"; import "@goauthentik/components/ak-textarea-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/Radio"; @@ -23,16 +24,15 @@ import { FlowsInstancesListDesignationEnum, IssuerModeEnum, OAuth2Provider, - PaginatedOAuthSourceList, ProvidersApi, - SourcesApi, SubModeEnum, } from "@goauthentik/api"; import { makeOAuth2PropertyMappingsSelector, oauth2PropertyMappingsProvider, -} from "./Oauth2PropertyMappings.js"; +} from "./OAuth2PropertyMappings.js"; +import { oauth2SourcesProvider } from "./OAuth2Sources.js"; export const clientTypeOptions = [ { @@ -127,8 +127,6 @@ export const redirectUriHelp = html`${redirectUriHelpMessages.map( @customElement("ak-provider-oauth2-form") export class OAuth2ProviderFormPage extends BaseProviderForm { - oauthSources?: PaginatedOAuthSourceList; - @state() showClientSecret = true; @@ -140,13 +138,6 @@ export class OAuth2ProviderFormPage extends BaseProviderForm { return provider; } - async load(): Promise { - this.oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({ - ordering: "name", - hasJwks: true, - }); - } - async send(data: OAuth2Provider): Promise { if (this.instance) { return new ProvidersApi(DEFAULT_CONFIG).providersOauth2Update({ @@ -344,24 +335,17 @@ export class OAuth2ProviderFormPage extends BaseProviderForm { label=${msg("Trusted OIDC Sources")} name="jwksSources" > - +

${msg( "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", )}

-

- ${msg("Hold control/command to select multiple items.")} -

`; diff --git a/web/src/admin/providers/oauth2/OAuth2Sources.ts b/web/src/admin/providers/oauth2/OAuth2Sources.ts new file mode 100644 index 0000000000..4adc6dd425 --- /dev/null +++ b/web/src/admin/providers/oauth2/OAuth2Sources.ts @@ -0,0 +1,21 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; + +import { SourcesApi } from "@goauthentik/api"; + +export async function oauth2SourcesProvider(page = 1, search = "") { + const oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({ + ordering: "name", + hasJwks: true, + pageSize: 20, + search: search.trim(), + page, + }); + + return { + pagination: oauthSources.pagination, + options: oauthSources.results.map((source) => [ + source.pk, + `${source.name} (${source.slug})`, + ]), + }; +} diff --git a/web/src/admin/providers/proxy/ProxyProviderForm.ts b/web/src/admin/providers/proxy/ProxyProviderForm.ts index 86c4c5ba64..1e741fb1c8 100644 --- a/web/src/admin/providers/proxy/ProxyProviderForm.ts +++ b/web/src/admin/providers/proxy/ProxyProviderForm.ts @@ -1,10 +1,12 @@ import "@goauthentik/admin/common/ak-crypto-certificate-search"; import "@goauthentik/admin/common/ak-flow-search/ak-flow-search"; import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm"; +import { oauth2SourcesProvider } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; import "@goauthentik/components/ak-toggle-group"; 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"; @@ -21,11 +23,9 @@ import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css"; import { FlowsInstancesListDesignationEnum, - PaginatedOAuthSourceList, ProvidersApi, ProxyMode, ProxyProvider, - SourcesApi, } from "@goauthentik/api"; import { @@ -48,15 +48,6 @@ export class ProxyProviderFormPage extends BaseProviderForm { return provider; } - async load(): Promise { - this.oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({ - ordering: "name", - hasJwks: true, - }); - } - - oauthSources?: PaginatedOAuthSourceList; - @state() showHttpBasic = true; @@ -412,24 +403,17 @@ ${this.instance?.skipPathRegex} - +

${msg( "JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.", )}

-

- ${msg("Hold control/command to select multiple items.")} -

`; diff --git a/web/src/admin/providers/radius/RadiusProviderForm.ts b/web/src/admin/providers/radius/RadiusProviderForm.ts index bb689d161e..9beb0e115f 100644 --- a/web/src/admin/providers/radius/RadiusProviderForm.ts +++ b/web/src/admin/providers/radius/RadiusProviderForm.ts @@ -159,9 +159,6 @@ export class RadiusProviderFormPage extends WithBrandConfig(BaseProviderForm -

- ${msg("Hold control/command to select multiple items.")} -

`; diff --git a/web/src/admin/sources/ldap/LDAPSourceForm.ts b/web/src/admin/sources/ldap/LDAPSourceForm.ts index 7ab4929018..3004abec19 100644 --- a/web/src/admin/sources/ldap/LDAPSourceForm.ts +++ b/web/src/admin/sources/ldap/LDAPSourceForm.ts @@ -34,6 +34,7 @@ async function propertyMappingsProvider(page = 1, search = "") { search: search.trim(), page, }); + return { pagination: propertyMappings.pagination, options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), diff --git a/web/src/admin/sources/plex/PlexSourceForm.ts b/web/src/admin/sources/plex/PlexSourceForm.ts index 4a01823af2..347954b76a 100644 --- a/web/src/admin/sources/plex/PlexSourceForm.ts +++ b/web/src/admin/sources/plex/PlexSourceForm.ts @@ -13,6 +13,8 @@ import { WithCapabilitiesConfig, } from "@goauthentik/elements/Interface/capabilitiesProvider"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; +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 { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; @@ -194,9 +196,6 @@ export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm -

- ${msg("Hold control/command to select multiple items.")} -

`; } diff --git a/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts b/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts index bd35b02674..ff438f6963 100644 --- a/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts +++ b/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts @@ -3,7 +3,6 @@ import { deviceTypeRestrictionPair } from "@goauthentik/admin/stages/authenticat import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import "@goauthentik/elements/Alert"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider"; -import { DataProvision } from "@goauthentik/elements/ak-dual-select/types"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/Radio"; @@ -23,6 +22,35 @@ import { UserVerificationEnum, } from "@goauthentik/api"; +async function stagesProvider(page = 1, search = "") { + const stages = await new StagesApi(DEFAULT_CONFIG).stagesAllList({ + ordering: "name", + pageSize: 20, + search: search.trim(), + page, + }); + + return { + pagination: stages.pagination, + options: stages.results.map((stage) => [stage.pk, `${stage.name} (${stage.verboseName})`]), + }; +} + +async function authenticatorWebauthnDeviceTypesListProvider(page = 1, search = "") { + const devicetypes = await new StagesApi( + DEFAULT_CONFIG, + ).stagesAuthenticatorWebauthnDeviceTypesList({ + pageSize: 20, + search: search.trim(), + page, + }); + + return { + pagination: devicetypes.pagination, + options: devicetypes.results.map(deviceTypeRestrictionPair), + }; +} + @customElement("ak-stage-authenticator-validate-form") export class AuthenticatorValidateStageForm extends BaseStageForm { async loadInstance(pk: string): Promise { @@ -177,21 +205,14 @@ export class AuthenticatorValidateStageForm extends BaseStageForm - +

${msg( "Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.", @@ -242,19 +263,7 @@ export class AuthenticatorValidateStageForm extends BaseStageForm => { - return new StagesApi(DEFAULT_CONFIG) - .stagesAuthenticatorWebauthnDeviceTypesList({ - page: page, - search: search, - }) - .then((results) => { - return { - pagination: results.pagination, - options: results.results.map(deviceTypeRestrictionPair), - }; - }); - }} + .provider=${authenticatorWebauthnDeviceTypesListProvider} .selected=${(this.instance?.webauthnAllowedDeviceTypesObj ?? []).map( deviceTypeRestrictionPair, )} diff --git a/web/src/admin/stages/identification/IdentificationStageForm.ts b/web/src/admin/stages/identification/IdentificationStageForm.ts index fb097e6e82..8b4c553c96 100644 --- a/web/src/admin/stages/identification/IdentificationStageForm.ts +++ b/web/src/admin/stages/identification/IdentificationStageForm.ts @@ -3,6 +3,8 @@ import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first, groupBy } from "@goauthentik/common/utils"; import "@goauthentik/elements/ak-checkbox-group/ak-checkbox-group.js"; +import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/SearchSelect"; @@ -15,7 +17,7 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { FlowsInstancesListDesignationEnum, IdentificationStage, - PaginatedSourceList, + Source, SourcesApi, Stage, StagesApi, @@ -23,6 +25,31 @@ import { UserFieldsEnum, } from "@goauthentik/api"; +async function sourcesProvider(page = 1, search = "") { + const sources = await new SourcesApi(DEFAULT_CONFIG).sourcesAllList({ + ordering: "slug", + pageSize: 20, + search: search.trim(), + page, + }); + + return { + pagination: sources.pagination, + options: sources.results + .filter((source) => source.component !== "") + .map((source) => [source.pk, source.name, source.name, source]), + }; +} + +async function makeSourcesSelector(instanceSources: string[] | undefined) { + const localSources = instanceSources ? new Set(instanceSources) : undefined; + + return localSources + ? ([pk, _]: DualSelectPair) => localSources.has(pk) + : ([_0, _1, _2, source]: DualSelectPair) => + source !== undefined && source.component === ""; +} + @customElement("ak-stage-identification-form") export class IdentificationStageForm extends BaseStageForm { static get styles() { @@ -42,14 +69,6 @@ export class IdentificationStageForm extends BaseStageForm }); } - async load(): Promise { - this.sources = await new SourcesApi(DEFAULT_CONFIG).sourcesAllList({ - ordering: "slug", - }); - } - - sources?: PaginatedSourceList; - async send(data: IdentificationStage): Promise { if (this.instance) { return new StagesApi(DEFAULT_CONFIG).stagesIdentificationUpdate({ @@ -213,33 +232,17 @@ export class IdentificationStageForm extends BaseStageForm ?required=${true} name="sources" > - +

${msg( "Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP.", )}

-

- ${msg("Hold control/command to select multiple items.")} -

- +

${msg( "Selected policies are executed when the stage is submitted to validate the data.", )}

-

- ${msg("Hold control/command to select multiple items.")} -

`;