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 f689a04f90f7..0eb85cf0d0ce 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,23 +1,23 @@ 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 { + propertyMappingsProvider, + propertyMappingsSelector, +} from "@goauthentik/admin/providers/oauth2/OAuth2ProviderFormHelpers.js"; import { IRedirectURIInput, akOAuthRedirectURIInput, } from "@goauthentik/admin/providers/oauth2/OAuth2ProviderRedirectURI"; import { - makeSourceSelector, oauth2SourcesProvider, + oauth2SourcesSelector, } 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"; @@ -252,10 +252,8 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel { .errorMessages=${errors?.propertyMappings ?? []} > @@ -309,7 +307,7 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel { > diff --git a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts index 867efbd0b383..6219beaa0aa8 100644 --- a/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts +++ b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts @@ -1,12 +1,12 @@ import "@goauthentik/admin/applications/wizard/ak-wizard-title"; import { - makeSourceSelector, oauth2SourcesProvider, + oauth2SourcesSelector, } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js"; import { - makeProxyPropertyMappingsSelector, - proxyPropertyMappingsProvider, -} from "@goauthentik/admin/providers/proxy/ProxyProviderPropertyMappings.js"; + propertyMappingsProvider, + propertyMappingsSelector, +} from "@goauthentik/admin/providers/proxy/ProxyProviderFormHelpers.js"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; import "@goauthentik/components/ak-switch-input"; @@ -147,8 +147,8 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel { name="propertyMappings" > diff --git a/web/src/admin/applications/wizard/methods/rac/ak-application-wizard-authentication-for-rac.ts b/web/src/admin/applications/wizard/methods/rac/ak-application-wizard-authentication-for-rac.ts index 27b7a2fb7ed0..15ab15fb910b 100644 --- a/web/src/admin/applications/wizard/methods/rac/ak-application-wizard-authentication-for-rac.ts +++ b/web/src/admin/applications/wizard/methods/rac/ak-application-wizard-authentication-for-rac.ts @@ -1,9 +1,9 @@ import "@goauthentik/admin/applications/wizard/ak-wizard-title"; import "@goauthentik/admin/common/ak-flow-search/ak-flow-search"; import { - makeRACPropertyMappingsSelector, - racPropertyMappingsProvider, -} from "@goauthentik/admin/providers/rac/RACPropertyMappings.js"; + propertyMappingsProvider, + propertyMappingsSelector, +} from "@goauthentik/admin/providers/rac/RACProviderFormHelpers.js"; import "@goauthentik/components/ak-text-input"; import "@goauthentik/elements/CodeMirror"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; @@ -70,10 +70,8 @@ export class ApplicationWizardAuthenticationByRAC extends BaseProviderPanel { name="propertyMappings" > diff --git a/web/src/admin/events/RuleForm.ts b/web/src/admin/events/RuleForm.ts index 24ec962f4d7c..7d89033649c6 100644 --- a/web/src/admin/events/RuleForm.ts +++ b/web/src/admin/events/RuleForm.ts @@ -1,7 +1,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { severityToLabel } from "@goauthentik/common/labels"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; -import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types"; import "@goauthentik/elements/forms/HorizontalFormElement"; import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; import "@goauthentik/elements/forms/Radio"; @@ -18,32 +17,12 @@ import { EventsApi, Group, NotificationRule, - NotificationTransport, PaginatedNotificationTransportList, 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, - }); +import { eventTransportsProvider, eventTransportsSelector } from "./RuleFormHelpers.js"; - return { - pagination: eventTransports.pagination, - options: eventTransports.results.map((transport) => [transport.pk, transport.name]), - }; -} - -export function makeTransportSelector(instanceTransports: string[] | undefined) { - const localTransports = instanceTransports ? new Set(instanceTransports) : undefined; - - return localTransports - ? ([pk, _]: DualSelectPair) => localTransports.has(pk) - : ([_0, _1, _2, stage]: DualSelectPair) => stage !== undefined; -} @customElement("ak-event-rule-form") export class RuleForm extends ModelForm { eventTransports?: PaginatedNotificationTransportList; @@ -126,7 +105,7 @@ export class RuleForm extends ModelForm { > diff --git a/web/src/admin/events/RuleFormHelpers.ts b/web/src/admin/events/RuleFormHelpers.ts new file mode 100644 index 000000000000..f941dc0182c4 --- /dev/null +++ b/web/src/admin/events/RuleFormHelpers.ts @@ -0,0 +1,42 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types"; + +import { EventsApi, NotificationTransport } from "@goauthentik/api"; + +const transportToSelect = (transport: NotificationTransport) => [transport.pk, transport.name]; + +export 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(transportToSelect), + }; +} + +export function eventTransportsSelector(instanceTransports: string[] | undefined) { + if (!instanceTransports) { + return async (transports: DualSelectPair[]) => + transports.filter( + ([_0, _1, _2, stage]: DualSelectPair) => stage !== undefined, + ); + } + + return async () => { + const transportsApi = new EventsApi(DEFAULT_CONFIG); + const transports = await Promise.allSettled( + instanceTransports.map((instanceId) => + transportsApi.eventsTransportsRetrieve({ uuid: instanceId }), + ), + ); + return transports + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(transportToSelect); + }; +} diff --git a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts index 86f2598db0ac..952685355abf 100644 --- a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts +++ b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderForm.ts @@ -1,8 +1,8 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm"; import { - googleWorkspacePropertyMappingsProvider, - makeGoogleWorkspacePropertyMappingsSelector, -} from "@goauthentik/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings"; + propertyMappingsProvider, + propertyMappingsSelector, +} from "@goauthentik/admin/providers/google_workspace/GoogleWorkspaceProviderFormHelpers.js"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; import "@goauthentik/elements/CodeMirror"; @@ -224,8 +224,8 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(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(mappingToSelect), + }; +} + +export function propertyMappingsSelector( + instanceMappings: string[] | undefined, + defaultSelection: string, +) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, mapping]: DualSelectPair) => + mapping?.managed === defaultSelection, + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderGoogleWorkspaceRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings.ts b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings.ts deleted file mode 100644 index 36996885b98f..000000000000 --- a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderPropertyMappings.ts +++ /dev/null @@ -1,30 +0,0 @@ -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/ldap/LDAPProviderFormHelpers.ts b/web/src/admin/providers/ldap/LDAPProviderFormHelpers.ts new file mode 100644 index 000000000000..08c66e423db6 --- /dev/null +++ b/web/src/admin/providers/ldap/LDAPProviderFormHelpers.ts @@ -0,0 +1,46 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { LDAPSourcePropertyMapping, PropertymappingsApi } from "@goauthentik/api"; + +const mappingToSelect = (m: LDAPSourcePropertyMapping) => [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourceLdapList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (transports: DualSelectPair[]) => + transports.filter( + ([_0, _1, _2, mapping]: DualSelectPair) => + mapping?.managed?.startsWith("goauthentik.io/sources/ldap/default") || + mapping?.managed?.startsWith("goauthentik.io/sources/ldap/ms"), + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsSourceLdapRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderForm.ts b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderForm.ts index de2eb396ebc8..523af5cc22d0 100644 --- a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderForm.ts +++ b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderForm.ts @@ -1,8 +1,8 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm"; import { - makeMicrosoftEntraPropertyMappingsSelector, - microsoftEntraPropertyMappingsProvider, -} from "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderPropertyMappings"; + propertyMappingsProvider, + propertyMappingsSelector, +} from "@goauthentik/admin/providers/microsoft_entra/MicrosoftEntraProviderFormHelpers.js"; 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"; @@ -213,8 +213,8 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(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(mappingToSelect), + }; +} + +export function propertyMappingsSelector( + instanceMappings: string[] | undefined, + defaultSelection: string, +) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, mapping]: DualSelectPair) => + mapping?.managed === defaultSelection, + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderMicrosoftEntraRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/oauth2/OAuth2PropertyMappings.ts b/web/src/admin/providers/oauth2/OAuth2PropertyMappings.ts deleted file mode 100644 index 2c4cc45e5bd3..000000000000 --- a/web/src/admin/providers/oauth2/OAuth2PropertyMappings.ts +++ /dev/null @@ -1,33 +0,0 @@ -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 const defaultScopes = [ - "goauthentik.io/providers/oauth2/scope-openid", - "goauthentik.io/providers/oauth2/scope-email", - "goauthentik.io/providers/oauth2/scope-profile", -]; - -export async function oauth2PropertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderScopeList({ - ordering: "scope_name", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((scope) => [scope.pk, scope.name, scope.name, scope]), - }; -} - -export function makeOAuth2PropertyMappingsSelector(instanceMappings: string[] | undefined) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, scope]: DualSelectPair) => - scope?.managed && defaultScopes.includes(scope?.managed); -} diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts index 65a81491474f..29ceb68792a3 100644 --- a/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts +++ b/web/src/admin/providers/oauth2/OAuth2ProviderForm.ts @@ -35,11 +35,8 @@ import { SubModeEnum, } from "@goauthentik/api"; -import { - makeOAuth2PropertyMappingsSelector, - oauth2PropertyMappingsProvider, -} from "./OAuth2PropertyMappings.js"; -import { makeSourceSelector, oauth2SourcesProvider } from "./OAuth2Sources.js"; +import { propertyMappingsProvider, propertyMappingsSelector } from "./OAuth2ProviderFormHelpers.js"; +import { oauth2SourcesProvider, oauth2SourcesSelector } from "./OAuth2Sources.js"; export const clientTypeOptions = [ { @@ -335,10 +332,8 @@ export class OAuth2ProviderFormPage extends BaseProviderForm { @@ -391,7 +386,7 @@ export class OAuth2ProviderFormPage extends BaseProviderForm { > diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderFormHelpers.ts b/web/src/admin/providers/oauth2/OAuth2ProviderFormHelpers.ts new file mode 100644 index 000000000000..676f42091b08 --- /dev/null +++ b/web/src/admin/providers/oauth2/OAuth2ProviderFormHelpers.ts @@ -0,0 +1,51 @@ +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 const defaultScopes = [ + "goauthentik.io/providers/oauth2/scope-openid", + "goauthentik.io/providers/oauth2/scope-email", + "goauthentik.io/providers/oauth2/scope-profile", +]; + +const mappingToSelect = (s: ScopeMapping) => [s.pk, s.name, s.name, s]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderScopeList({ + ordering: "scope_name", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, scope]: DualSelectPair) => + scope?.managed && defaultScopes.includes(scope?.managed), + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderScopeRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/oauth2/OAuth2Sources.ts b/web/src/admin/providers/oauth2/OAuth2Sources.ts index f6ecde10f2ea..69743223ecaa 100644 --- a/web/src/admin/providers/oauth2/OAuth2Sources.ts +++ b/web/src/admin/providers/oauth2/OAuth2Sources.ts @@ -3,6 +3,13 @@ import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types"; import { OAuthSource, SourcesApi } from "@goauthentik/api"; +const sourceToSelect = (source: OAuthSource) => [ + source.pk, + `${source.name} (${source.slug})`, + source.name, + source, +]; + export async function oauth2SourcesProvider(page = 1, search = "") { const oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({ ordering: "name", @@ -14,17 +21,29 @@ export async function oauth2SourcesProvider(page = 1, search = "") { return { pagination: oauthSources.pagination, - options: oauthSources.results.map((source) => [ - source.pk, - `${source.name} (${source.slug})`, - ]), + options: oauthSources.results.map(sourceToSelect), }; } -export function makeSourceSelector(instanceSources: string[] | undefined) { - const localSources = instanceSources ? new Set(instanceSources) : undefined; +export function oauth2SourcesSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, source]: DualSelectPair) => source !== undefined, + ); + } + + return async () => { + const oauthSources = new SourcesApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + oauthSources.sourcesOauthRetrieve({ slug: instanceId }), + ), + ); - return localSources - ? ([pk, _]: DualSelectPair) => localSources.has(pk) - : ([_0, _1, _2, prompt]: DualSelectPair) => prompt !== undefined; + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(sourceToSelect); + }; } diff --git a/web/src/admin/providers/proxy/ProxyProviderForm.ts b/web/src/admin/providers/proxy/ProxyProviderForm.ts index 86c34969f171..aa93ea08adbf 100644 --- a/web/src/admin/providers/proxy/ProxyProviderForm.ts +++ b/web/src/admin/providers/proxy/ProxyProviderForm.ts @@ -2,8 +2,8 @@ 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 { - makeSourceSelector, oauth2SourcesProvider, + oauth2SourcesSelector, } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; @@ -30,10 +30,7 @@ import { ProxyProvider, } from "@goauthentik/api"; -import { - makeProxyPropertyMappingsSelector, - proxyPropertyMappingsProvider, -} from "./ProxyProviderPropertyMappings.js"; +import { propertyMappingsProvider, propertyMappingsSelector } from "./ProxyProviderFormHelpers.js"; @customElement("ak-provider-proxy-form") export class ProxyProviderFormPage extends BaseProviderForm { @@ -302,10 +299,8 @@ export class ProxyProviderFormPage extends BaseProviderForm { name="propertyMappings" > @@ -394,7 +389,7 @@ ${this.instance?.skipPathRegex} diff --git a/web/src/admin/providers/proxy/ProxyProviderFormHelpers.ts b/web/src/admin/providers/proxy/ProxyProviderFormHelpers.ts new file mode 100644 index 000000000000..fa805d7ec4ea --- /dev/null +++ b/web/src/admin/providers/proxy/ProxyProviderFormHelpers.ts @@ -0,0 +1,45 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { PropertymappingsApi, ScopeMapping } from "@goauthentik/api"; + +const mappingToSelect = (s: ScopeMapping) => [s.pk, s.name, s.name, s]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderScopeList({ + ordering: "scope_name", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, scope]: DualSelectPair) => + !(scope?.managed ?? "").startsWith("goauthentik.io/providers"), + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderScopeRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/proxy/ProxyProviderPropertyMappings.ts b/web/src/admin/providers/proxy/ProxyProviderPropertyMappings.ts deleted file mode 100644 index d90cc8ea71a2..000000000000 --- a/web/src/admin/providers/proxy/ProxyProviderPropertyMappings.ts +++ /dev/null @@ -1,27 +0,0 @@ -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 proxyPropertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderScopeList({ - ordering: "scope_name", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((scope) => [scope.pk, scope.name, scope.name, scope]), - }; -} - -export function makeProxyPropertyMappingsSelector(mappings?: string[]) { - const localMappings = mappings ? new Set(mappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, scope]: DualSelectPair) => - !(scope?.managed ?? "").startsWith("goauthentik.io/providers"); -} diff --git a/web/src/admin/providers/rac/EndpointForm.ts b/web/src/admin/providers/rac/EndpointForm.ts index b464c0aac073..2c039b85fdd3 100644 --- a/web/src/admin/providers/rac/EndpointForm.ts +++ b/web/src/admin/providers/rac/EndpointForm.ts @@ -15,10 +15,7 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { AuthModeEnum, Endpoint, ProtocolEnum, RacApi } from "@goauthentik/api"; -import { - makeRACPropertyMappingsSelector, - racPropertyMappingsProvider, -} from "./RACPropertyMappings.js"; +import { propertyMappingsProvider, propertyMappingsSelector } from "./RACProviderFormHelpers.js"; @customElement("ak-rac-endpoint-form") export class EndpointForm extends ModelForm { @@ -114,8 +111,8 @@ export class EndpointForm extends ModelForm { diff --git a/web/src/admin/providers/rac/RACPropertyMappings.ts b/web/src/admin/providers/rac/RACPropertyMappings.ts deleted file mode 100644 index 1b26c74041e8..000000000000 --- a/web/src/admin/providers/rac/RACPropertyMappings.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; - -import { PropertymappingsApi } from "@goauthentik/api"; - -export async function racPropertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderRacList({ - ordering: "name", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((mapping) => [mapping.pk, mapping.name]), - }; -} - -export function makeRACPropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = new Set(instanceMappings ?? []); - return ([pk, _]: DualSelectPair) => localMappings.has(pk); -} diff --git a/web/src/admin/providers/rac/RACProviderForm.ts b/web/src/admin/providers/rac/RACProviderForm.ts index e7d3f9428510..73b4c88c82f2 100644 --- a/web/src/admin/providers/rac/RACProviderForm.ts +++ b/web/src/admin/providers/rac/RACProviderForm.ts @@ -19,10 +19,7 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { FlowsInstancesListDesignationEnum, ProvidersApi, RACProvider } from "@goauthentik/api"; -import { - makeRACPropertyMappingsSelector, - racPropertyMappingsProvider, -} from "./RACPropertyMappings.js"; +import { propertyMappingsProvider, propertyMappingsSelector } from "./RACProviderFormHelpers.js"; @customElement("ak-provider-rac-form") export class RACProviderFormPage extends ModelForm { @@ -127,10 +124,8 @@ export class RACProviderFormPage extends ModelForm { name="propertyMappings" > diff --git a/web/src/admin/providers/rac/RACProviderFormHelpers.ts b/web/src/admin/providers/rac/RACProviderFormHelpers.ts new file mode 100644 index 000000000000..3da5619cd109 --- /dev/null +++ b/web/src/admin/providers/rac/RACProviderFormHelpers.ts @@ -0,0 +1,41 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { PropertymappingsApi, RACPropertyMapping } from "@goauthentik/api"; + +const mappingToSelect = (m: RACPropertyMapping) => [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderRacList({ + ordering: "name", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (_mappings: DualSelectPair[]) => []; + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderRacRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/radius/RadiusProviderForm.ts b/web/src/admin/providers/radius/RadiusProviderForm.ts index a1d7aeacf951..b5f7cf921338 100644 --- a/web/src/admin/providers/radius/RadiusProviderForm.ts +++ b/web/src/admin/providers/radius/RadiusProviderForm.ts @@ -4,7 +4,6 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm" import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils"; import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider"; -import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/SearchSelect"; @@ -14,35 +13,9 @@ import { TemplateResult, html } from "lit"; import { ifDefined } from "lit-html/directives/if-defined.js"; import { customElement } from "lit/decorators.js"; -import { - FlowsInstancesListDesignationEnum, - PropertymappingsApi, - ProvidersApi, - RadiusProvider, - RadiusProviderPropertyMapping, -} from "@goauthentik/api"; +import { FlowsInstancesListDesignationEnum, ProvidersApi, RadiusProvider } from "@goauthentik/api"; -export async function radiusPropertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderRadiusList({ - ordering: "name", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -export function makeRadiusPropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, _]: DualSelectPair) => []; -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./RadiusProviderFormHelpers.js"; @customElement("ak-provider-radius-form") export class RadiusProviderFormPage extends WithBrandConfig(BaseProviderForm) { @@ -155,10 +128,8 @@ export class RadiusProviderFormPage extends WithBrandConfig(BaseProviderForm diff --git a/web/src/admin/providers/radius/RadiusProviderFormHelpers.ts b/web/src/admin/providers/radius/RadiusProviderFormHelpers.ts new file mode 100644 index 000000000000..33843d220156 --- /dev/null +++ b/web/src/admin/providers/radius/RadiusProviderFormHelpers.ts @@ -0,0 +1,41 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { PropertymappingsApi, RadiusProviderPropertyMapping } from "@goauthentik/api"; + +const mappingToSelect = (m: RadiusProviderPropertyMapping) => [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderRadiusList({ + ordering: "name", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (_mappings: DualSelectPair[]) => []; + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderRadiusRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/saml/SAMLProviderForm.ts b/web/src/admin/providers/saml/SAMLProviderForm.ts index ef35d2960b3f..6917c9ae9c63 100644 --- a/web/src/admin/providers/saml/SAMLProviderForm.ts +++ b/web/src/admin/providers/saml/SAMLProviderForm.ts @@ -9,7 +9,6 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm" 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 { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/Radio"; @@ -31,28 +30,7 @@ import { SpBindingEnum, } from "@goauthentik/api"; -export async function samlPropertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderSamlList({ - ordering: "saml_name", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -export function makeSAMLPropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, mapping]: DualSelectPair) => - mapping?.managed?.startsWith("goauthentik.io/providers/saml"); -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./SAMLProviderFormHelpers.js"; @customElement("ak-provider-saml-form") export class SAMLProviderFormPage extends BaseProviderForm { @@ -303,10 +281,8 @@ export class SAMLProviderFormPage extends BaseProviderForm { name="propertyMappings" > diff --git a/web/src/admin/providers/saml/SAMLProviderFormHelpers.ts b/web/src/admin/providers/saml/SAMLProviderFormHelpers.ts new file mode 100644 index 000000000000..54e8c789e254 --- /dev/null +++ b/web/src/admin/providers/saml/SAMLProviderFormHelpers.ts @@ -0,0 +1,44 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { PropertymappingsApi, SAMLPropertyMapping } from "@goauthentik/api"; + +const mappingToSelect = (m: SAMLPropertyMapping) => [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderSamlList({ + ordering: "saml_name", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter(([_0, _1, _2, mapping]: DualSelectPair) => + mapping?.managed?.startsWith("goauthentik.io/providers/saml"), + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderSamlRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/providers/scim/SCIMProviderForm.ts b/web/src/admin/providers/scim/SCIMProviderForm.ts index 5bc15c2bf8cf..ff2074f2986c 100644 --- a/web/src/admin/providers/scim/SCIMProviderForm.ts +++ b/web/src/admin/providers/scim/SCIMProviderForm.ts @@ -2,7 +2,6 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm" 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 { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/Radio"; @@ -17,37 +16,11 @@ import { CoreApi, CoreGroupsListRequest, Group, - PropertymappingsApi, ProvidersApi, - SCIMMapping, SCIMProvider, } from "@goauthentik/api"; -export async function scimPropertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsProviderScimList({ - ordering: "managed", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -export function makeSCIMPropertyMappingsSelector( - instanceMappings: string[] | undefined, - defaultSelected: string, -) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, mapping]: DualSelectPair) => - mapping?.managed === defaultSelected; -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./SCIMProviderFormHelpers.js"; @customElement("ak-provider-scim-form") export class SCIMProviderFormPage extends BaseProviderForm { @@ -189,8 +162,8 @@ export class SCIMProviderFormPage extends BaseProviderForm { label=${msg("User Property Mappings")} name="propertyMappings"> { label=${msg("Group Property Mappings")} name="propertyMappingsGroup"> [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsProviderScimList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector( + instanceMappings: string[] | undefined, + defaultSelected: string, +) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, mapping]: DualSelectPair) => + mapping?.managed === defaultSelected, + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsProviderScimRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/sources/kerberos/KerberosSourceForm.ts b/web/src/admin/sources/kerberos/KerberosSourceForm.ts index a388c9ef33c6..e7d8c2ef1b2b 100644 --- a/web/src/admin/sources/kerberos/KerberosSourceForm.ts +++ b/web/src/admin/sources/kerberos/KerberosSourceForm.ts @@ -15,7 +15,6 @@ import { WithCapabilitiesConfig, } from "@goauthentik/elements/Interface/capabilitiesProvider"; 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"; @@ -29,37 +28,12 @@ import { FlowsInstancesListDesignationEnum, GroupMatchingModeEnum, KerberosSource, - KerberosSourcePropertyMapping, KerberosSourceRequest, - PropertymappingsApi, SourcesApi, UserMatchingModeEnum, } from "@goauthentik/api"; -async function propertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsSourceKerberosList({ - ordering: "managed", - pageSize: 20, - search: search.trim(), - page, - }); - - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -function makePropertyMappingsSelector(object: string, instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, mapping]: DualSelectPair) => - object == "user" && - mapping?.managed?.startsWith("goauthentik.io/sources/kerberos/user/default/"); -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./KerberosSourceFormHelpers.js"; @customElement("ak-source-kerberos-form") export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm) { @@ -323,7 +297,7 @@ export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourceKerberosList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(object: string, instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, mapping]: DualSelectPair) => + object == "user" && + mapping?.managed?.startsWith("goauthentik.io/sources/kerberos/user/default/"), + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsSourceKerberosRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/sources/ldap/LDAPSourceForm.ts b/web/src/admin/sources/ldap/LDAPSourceForm.ts index ecca7d4a7df5..0905e192ce78 100644 --- a/web/src/admin/sources/ldap/LDAPSourceForm.ts +++ b/web/src/admin/sources/ldap/LDAPSourceForm.ts @@ -4,7 +4,6 @@ import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm"; 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 { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/SearchSelect"; @@ -19,36 +18,11 @@ import { CoreGroupsListRequest, Group, LDAPSource, - LDAPSourcePropertyMapping, LDAPSourceRequest, - PropertymappingsApi, SourcesApi, } from "@goauthentik/api"; -async function propertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsSourceLdapList({ - ordering: "managed", - pageSize: 20, - search: search.trim(), - page, - }); - - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -function makePropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, mapping]: DualSelectPair) => - mapping?.managed?.startsWith("goauthentik.io/sources/ldap/default") || - mapping?.managed?.startsWith("goauthentik.io/sources/ldap/ms"); -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./LDAPSourceFormHelpers.js"; @customElement("ak-source-ldap-form") export class LDAPSourceForm extends BaseSourceForm { @@ -296,7 +270,7 @@ export class LDAPSourceForm extends BaseSourceForm { > { > [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourceLdapList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (transports: DualSelectPair[]) => + transports.filter( + ([_0, _1, _2, mapping]: DualSelectPair) => + mapping?.managed?.startsWith("goauthentik.io/sources/ldap/default") || + mapping?.managed?.startsWith("goauthentik.io/sources/ldap/ms"), + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsSourceLdapRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/sources/oauth/OAuthSourceForm.ts b/web/src/admin/sources/oauth/OAuthSourceForm.ts index a1b7b2ce082a..04fce8b888b5 100644 --- a/web/src/admin/sources/oauth/OAuthSourceForm.ts +++ b/web/src/admin/sources/oauth/OAuthSourceForm.ts @@ -14,7 +14,6 @@ import { WithCapabilitiesConfig, } from "@goauthentik/elements/Interface/capabilitiesProvider"; 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"; @@ -28,36 +27,14 @@ import { FlowsInstancesListDesignationEnum, GroupMatchingModeEnum, OAuthSource, - OAuthSourcePropertyMapping, OAuthSourceRequest, - PropertymappingsApi, ProviderTypeEnum, SourceType, SourcesApi, UserMatchingModeEnum, } from "@goauthentik/api"; -async function propertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsSourceOauthList({ - ordering: "managed", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -function makePropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, _]: DualSelectPair) => false; -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./OAuthSourceFormHelpers.js"; @customElement("ak-source-oauth-form") export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm) { @@ -467,7 +444,7 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourceOauthList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter( + ([_0, _1, _2, _3]: DualSelectPair) => false, + ); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsSourceOauthRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/sources/plex/PlexSourceForm.ts b/web/src/admin/sources/plex/PlexSourceForm.ts index 347954b76a5e..08f9c30ccd39 100644 --- a/web/src/admin/sources/plex/PlexSourceForm.ts +++ b/web/src/admin/sources/plex/PlexSourceForm.ts @@ -15,7 +15,6 @@ import { 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"; import "@goauthentik/elements/forms/SearchSelect"; @@ -29,33 +28,11 @@ import { FlowsInstancesListDesignationEnum, GroupMatchingModeEnum, PlexSource, - PlexSourcePropertyMapping, - PropertymappingsApi, SourcesApi, UserMatchingModeEnum, } from "@goauthentik/api"; -async function propertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsSourcePlexList({ - ordering: "managed", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -function makePropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, _]: DualSelectPair) => false; -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./PlexSourceFormHelpers.js"; @customElement("ak-source-plex-form") export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm) { @@ -420,7 +397,7 @@ export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourcePlexList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter(([_0, _1, _2, _3]: DualSelectPair) => false); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsSourcePlexRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/sources/saml/SAMLSourceForm.ts b/web/src/admin/sources/saml/SAMLSourceForm.ts index 599928c613c4..dc1dfc09105e 100644 --- a/web/src/admin/sources/saml/SAMLSourceForm.ts +++ b/web/src/admin/sources/saml/SAMLSourceForm.ts @@ -13,7 +13,6 @@ import { WithCapabilitiesConfig, } from "@goauthentik/elements/Interface/capabilitiesProvider"; 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/Radio"; @@ -30,35 +29,13 @@ import { FlowsInstancesListDesignationEnum, GroupMatchingModeEnum, NameIdPolicyEnum, - PropertymappingsApi, SAMLSource, - SAMLSourcePropertyMapping, SignatureAlgorithmEnum, SourcesApi, UserMatchingModeEnum, } from "@goauthentik/api"; -async function propertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsSourceSamlList({ - ordering: "managed", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -function makePropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, _]: DualSelectPair) => false; -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./SAMLSourceFormHelpers.js"; @customElement("ak-source-saml-form") export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm) { @@ -532,7 +509,7 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourceSamlList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter(([_0, _1, _2, _4]: DualSelectPair) => false); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsSourceSamlRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/sources/scim/SCIMSourceForm.ts b/web/src/admin/sources/scim/SCIMSourceForm.ts index c156d2166394..61546f6cdb4f 100644 --- a/web/src/admin/sources/scim/SCIMSourceForm.ts +++ b/web/src/admin/sources/scim/SCIMSourceForm.ts @@ -3,7 +3,6 @@ import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm"; 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 { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; @@ -12,35 +11,9 @@ import { TemplateResult, html } from "lit"; import { customElement } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; -import { - PropertymappingsApi, - SCIMSource, - SCIMSourcePropertyMapping, - SCIMSourceRequest, - SourcesApi, -} from "@goauthentik/api"; +import { SCIMSource, SCIMSourceRequest, SourcesApi } from "@goauthentik/api"; -async function propertyMappingsProvider(page = 1, search = "") { - const propertyMappings = await new PropertymappingsApi( - DEFAULT_CONFIG, - ).propertymappingsSourceScimList({ - ordering: "managed", - pageSize: 20, - search: search.trim(), - page, - }); - return { - pagination: propertyMappings.pagination, - options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), - }; -} - -function makePropertyMappingsSelector(instanceMappings?: string[]) { - const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; - return localMappings - ? ([pk, _]: DualSelectPair) => localMappings.has(pk) - : ([_0, _1, _2, _]: DualSelectPair) => false; -} +import { propertyMappingsProvider, propertyMappingsSelector } from "./SCIMSourceFormHelpers.js"; @customElement("ak-source-scim-form") export class SCIMSourceForm extends BaseSourceForm { @@ -104,7 +77,7 @@ export class SCIMSourceForm extends BaseSourceForm { > { > [m.pk, m.name, m.name, m]; + +export async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourceScimList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map(mappingToSelect), + }; +} + +export function propertyMappingsSelector(instanceMappings?: string[]) { + if (!instanceMappings) { + return async (mappings: DualSelectPair[]) => + mappings.filter(([_0, _1, _2, _3]: DualSelectPair) => false); + } + + return async () => { + const pm = new PropertymappingsApi(DEFAULT_CONFIG); + const mappings = await Promise.allSettled( + instanceMappings.map((instanceId) => + pm.propertymappingsSourceScimRetrieve({ pmUuid: instanceId }), + ), + ); + + return mappings + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(mappingToSelect); + }; +} diff --git a/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts b/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts index 16c94119733b..7fed4be5ccb6 100644 --- a/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts +++ b/web/src/admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts @@ -4,7 +4,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import "@goauthentik/elements/Alert"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider"; -import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/Radio"; @@ -20,47 +19,15 @@ import { DeviceClassesEnum, NotConfiguredActionEnum, PaginatedStageList, - Stage, StagesApi, 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})`]), - }; -} - -export function makeStageSelector(instanceStages: string[] | undefined) { - const localStages = instanceStages ? new Set(instanceStages) : undefined; - - return localStages - ? ([pk, _]: DualSelectPair) => localStages.has(pk) - : ([_0, _1, _2, stage]: DualSelectPair) => stage !== undefined; -} - -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), - }; -} +import { + authenticatorWebauthnDeviceTypesListProvider, + stagesProvider, + stagesSelector, +} from "./AuthenticatorValidateStageFormHelpers.js"; @customElement("ak-stage-authenticator-validate-form") export class AuthenticatorValidateStageForm extends BaseStageForm { @@ -218,7 +185,7 @@ export class AuthenticatorValidateStageForm extends BaseStageForm [stage.pk, `${stage.name} (${stage.verboseName})`]; + +export 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(stageToSelect), + }; +} + +export function stagesSelector(instanceStages: string[] | undefined) { + if (!instanceStages) { + return async (stages: DualSelectPair[]) => + stages.filter(([_0, _1, _2, stage]: DualSelectPair) => stage !== undefined); + } + return async () => { + const stagesApi = new StagesApi(DEFAULT_CONFIG); + const stages = await Promise.allSettled( + instanceStages.map((instanceId) => + stagesApi.stagesAllRetrieve({ stageUuid: instanceId }), + ), + ); + return stages + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(stageToSelect); + }; +} + +export 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), + }; +} diff --git a/web/src/admin/stages/identification/IdentificationStageForm.ts b/web/src/admin/stages/identification/IdentificationStageForm.ts index 7a20af84d612..bc2445ddbb2c 100644 --- a/web/src/admin/stages/identification/IdentificationStageForm.ts +++ b/web/src/admin/stages/identification/IdentificationStageForm.ts @@ -4,7 +4,6 @@ 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"; @@ -17,8 +16,6 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { FlowsInstancesListDesignationEnum, IdentificationStage, - Source, - SourcesApi, Stage, StagesApi, StagesCaptchaListRequest, @@ -26,31 +23,7 @@ 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]), - }; -} - -function makeSourcesSelector(instanceSources: string[] | undefined) { - const localSources = instanceSources ? new Set(instanceSources) : undefined; - - return localSources - ? ([pk, _]: DualSelectPair) => localSources.has(pk) - : // Creating a new instance, auto-select built-in source only when no other sources exist - ([_0, _1, _2, source]: DualSelectPair) => - source !== undefined && source.component === ""; -} +import { sourcesProvider, sourcesSelector } from "./IdentificationStageFormHelpers.js"; @customElement("ak-stage-identification-form") export class IdentificationStageForm extends BaseStageForm { @@ -259,7 +232,7 @@ export class IdentificationStageForm extends BaseStageForm > diff --git a/web/src/admin/stages/identification/IdentificationStageFormHelpers.ts b/web/src/admin/stages/identification/IdentificationStageFormHelpers.ts new file mode 100644 index 000000000000..a6a31590486b --- /dev/null +++ b/web/src/admin/stages/identification/IdentificationStageFormHelpers.ts @@ -0,0 +1,39 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { Source, SourcesApi } from "@goauthentik/api"; + +const sourceToSelect = (source: Source) => [source.pk, source.name, source.name, source]; + +export 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(sourceToSelect), + }; +} + +export function sourcesSelector(instanceSources: string[] | undefined) { + if (!instanceSources) { + return async (sources: DualSelectPair[]) => + sources.filter(([_0, _1, _2, source]: DualSelectPair) => source !== undefined); + } + return async () => { + const sourcesApi = new SourcesApi(DEFAULT_CONFIG); + const sources = await Promise.allSettled( + instanceSources.map((instanceId) => + sourcesApi.sourcesAllRetrieve({ slug: instanceId }), + ), + ); + return sources + .filter((s) => s.status === "fulfilled") + .map((s) => s.value) + .map(sourceToSelect); + }; +} diff --git a/web/src/admin/stages/prompt/PromptStageForm.ts b/web/src/admin/stages/prompt/PromptStageForm.ts index 2fb9e6e2b63f..845f12d73d1b 100644 --- a/web/src/admin/stages/prompt/PromptStageForm.ts +++ b/web/src/admin/stages/prompt/PromptStageForm.ts @@ -3,67 +3,23 @@ import "@goauthentik/admin/stages/prompt/PromptForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { PFSize } from "@goauthentik/common/enums"; 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/ModalForm"; -import { msg, str } from "@lit/localize"; +import { msg } from "@lit/localize"; import { TemplateResult, html, nothing } from "lit"; import { customElement } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; -import { PoliciesApi, Policy, Prompt, PromptStage, StagesApi } from "@goauthentik/api"; +import { PromptStage, StagesApi } from "@goauthentik/api"; -async function promptFieldsProvider(page = 1, search = "") { - const prompts = await new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsList({ - ordering: "field_name", - pageSize: 20, - search: search.trim(), - page, - }); - - return { - pagination: prompts.pagination, - options: prompts.results.map((prompt) => [ - prompt.pk, - msg(str`${prompt.name} ("${prompt.fieldKey}", of type ${prompt.type})`), - ]), - }; -} - -function makeFieldSelector(instanceFields: string[] | undefined) { - const localFields = instanceFields ? new Set(instanceFields) : undefined; - - return localFields - ? ([pk, _]: DualSelectPair) => localFields.has(pk) - : ([_0, _1, _2, prompt]: DualSelectPair) => prompt !== undefined; -} - -async function policiesProvider(page = 1, search = "") { - const policies = await new PoliciesApi(DEFAULT_CONFIG).policiesAllList({ - ordering: "name", - pageSize: 20, - search: search.trim(), - page, - }); - - return { - pagination: policies.pagination, - options: policies.results.map((policy) => [ - policy.pk, - `${policy.name} (${policy.verboseName})`, - ]), - }; -} - -function makePoliciesSelector(instancePolicies: string[] | undefined) { - const localPolicies = instancePolicies ? new Set(instancePolicies) : undefined; - - return localPolicies - ? ([pk, _]: DualSelectPair) => localPolicies.has(pk) - : ([_0, _1, _2, policy]: DualSelectPair) => policy !== undefined; -} +import { + policiesProvider, + policiesSelector, + promptFieldsProvider, + promptFieldsSelector, +} from "./PromptStageFormHelpers.js"; @customElement("ak-stage-prompt-form") export class PromptStageForm extends BaseStageForm { @@ -110,7 +66,7 @@ export class PromptStageForm extends BaseStageForm { > @@ -135,7 +91,7 @@ export class PromptStageForm extends BaseStageForm { > diff --git a/web/src/admin/stages/prompt/PromptStageFormHelpers.ts b/web/src/admin/stages/prompt/PromptStageFormHelpers.ts new file mode 100644 index 000000000000..379649864a20 --- /dev/null +++ b/web/src/admin/stages/prompt/PromptStageFormHelpers.ts @@ -0,0 +1,82 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; + +import { msg, str } from "@lit/localize"; + +import { PoliciesApi, Policy, Prompt, StagesApi } from "@goauthentik/api"; + +const promptToSelect = (p: Prompt) => [ + p.pk, + msg(str`${p.name} ("${p.fieldKey}", of type ${p.type})`), + p.name, + p, +]; + +export async function promptFieldsProvider(page = 1, search = "") { + const prompts = await new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsList({ + ordering: "field_name,order", + pageSize: 20, + search: search.trim(), + page, + }); + + return { + pagination: prompts.pagination, + options: prompts.results.map(promptToSelect), + }; +} + +export function promptFieldsSelector(instanceFields: string[] | undefined) { + if (!instanceFields) { + return async (options: DualSelectPair[]) => + options.filter(([_0, _1, _2, prompt]: DualSelectPair) => prompt !== undefined); + } + return async () => { + const stages = new StagesApi(DEFAULT_CONFIG); + const prompts = await Promise.allSettled( + instanceFields.map((instanceId) => + stages.stagesPromptPromptsRetrieve({ promptUuid: instanceId }), + ), + ); + return prompts + .filter((p) => p.status === "fulfilled") + .map((p) => p.value) + .map(promptToSelect); + }; +} + +const policyToSelect = (p: Policy) => [p.pk, `${p.name} (${p.verboseName})`, p.name, p]; + +export async function policiesProvider(page = 1, search = "") { + const policies = await new PoliciesApi(DEFAULT_CONFIG).policiesAllList({ + ordering: "name", + pageSize: 20, + search: search.trim(), + page, + }); + + return { + pagination: policies.pagination, + options: policies.results.map(policyToSelect), + }; +} + +export function policiesSelector(instancePolicies: string[] | undefined) { + if (!instancePolicies) { + return async (options: DualSelectPair[]) => + options.filter(([_0, _1, _2, policy]: DualSelectPair) => policy !== undefined); + } + + return async () => { + const policy = new PoliciesApi(DEFAULT_CONFIG); + const policies = await Promise.allSettled( + instancePolicies.map((instanceId) => + policy.policiesAllRetrieve({ policyUuid: instanceId }), + ), + ); + return policies + .filter((p) => p.status === "fulfilled") + .map((p) => p.value) + .map(policyToSelect); + }; +} diff --git a/web/src/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.ts b/web/src/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.ts index 59037776ef2d..2a4d24872834 100644 --- a/web/src/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.ts +++ b/web/src/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.ts @@ -23,7 +23,7 @@ export class AkDualSelectDynamic extends AkDualSelectProvider { * @attr */ @property({ attribute: false }) - selector: ([key, _]: DualSelectPair) => boolean = ([_key, _]) => false; + selector: (_: DualSelectPair[]) => Promise = async (_) => Promise.resolve([]); private firstUpdateHasRun = false; @@ -33,9 +33,9 @@ export class AkDualSelectDynamic extends AkDualSelectProvider { // the selected list with the contents derived from the selector. if (!this.firstUpdateHasRun && this.options.length > 0) { this.firstUpdateHasRun = true; - this.selected = Array.from( - new Set([...this.selected, ...this.options.filter(this.selector)]), - ); + this.selector(this.options).then((selected) => { + this.selected = selected; + }); } }