Merge branch 'main' into web/add-htmltagmaps-to-activate-lit-analyzer
* main: core: bump setuptools from 69.5.1 to 70.0.0 (#10503) web: replace multi-select with dual-select for all propertyMapping invocations (#9359) web: enable custom-element-manifest and DOM/JS integration checking. (#10177)
This commit is contained in:
@ -23,7 +23,7 @@
|
||||
"quotes": ["error", "double", { "avoidEscape": true }],
|
||||
"semi": ["error", "always"],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"sonarjs/cognitive-complexity": ["error", 9],
|
||||
"sonarjs/cognitive-complexity": ["warn", 9],
|
||||
"sonarjs/no-duplicate-string": "off",
|
||||
"sonarjs/no-nested-template-literals": "off"
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ const eslintConfig = {
|
||||
"quotes": ["error", "double", { avoidEscape: true }],
|
||||
"semi": ["error", "always"],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"sonarjs/cognitive-complexity": ["error", 9],
|
||||
"sonarjs/cognitive-complexity": ["warn", 9],
|
||||
"sonarjs/no-duplicate-string": "off",
|
||||
"sonarjs/no-nested-template-literals": "off",
|
||||
},
|
||||
@ -72,5 +72,6 @@ const formatter = await eslint.loadFormatter("stylish");
|
||||
const resultText = formatter.format(results);
|
||||
const errors = results.reduce((acc, result) => acc + result.errorCount, 0);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(resultText);
|
||||
process.exit(errors > 1 ? 1 : 0);
|
||||
|
@ -3,11 +3,14 @@ import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
|
||||
import {
|
||||
clientTypeOptions,
|
||||
defaultScopes,
|
||||
issuerModeOptions,
|
||||
redirectUriHelp,
|
||||
subjectModeOptions,
|
||||
} from "@goauthentik/admin/providers/oauth2/OAuth2ProviderForm";
|
||||
import {
|
||||
makeOAuth2PropertyMappingsSelector,
|
||||
oauth2PropertyMappingsProvider,
|
||||
} from "@goauthentik/admin/providers/oauth2/Oauth2PropertyMappings.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";
|
||||
@ -15,6 +18,7 @@ import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
@ -23,17 +27,8 @@ import { customElement, state } from "@lit/reactive-element/decorators.js";
|
||||
import { html, nothing } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
ClientTypeEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PropertymappingsApi,
|
||||
SourcesApi,
|
||||
} from "@goauthentik/api";
|
||||
import {
|
||||
type OAuth2Provider,
|
||||
type PaginatedOAuthSourceList,
|
||||
type PaginatedScopeMappingList,
|
||||
} from "@goauthentik/api";
|
||||
import { ClientTypeEnum, FlowsInstancesListDesignationEnum, SourcesApi } from "@goauthentik/api";
|
||||
import { type OAuth2Provider, type PaginatedOAuthSourceList } from "@goauthentik/api";
|
||||
|
||||
import BaseProviderPanel from "../BaseProviderPanel";
|
||||
|
||||
@ -42,22 +37,11 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||
@state()
|
||||
showClientSecret = true;
|
||||
|
||||
@state()
|
||||
propertyMappings?: PaginatedScopeMappingList;
|
||||
|
||||
@state()
|
||||
oauthSources?: PaginatedOAuthSourceList;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScopeList({
|
||||
ordering: "scope_name",
|
||||
})
|
||||
.then((propertyMappings: PaginatedScopeMappingList) => {
|
||||
this.propertyMappings = propertyMappings;
|
||||
});
|
||||
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthList({
|
||||
ordering: "name",
|
||||
@ -222,36 +206,19 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||
name="propertyMappings"
|
||||
.errorMessages=${errors?.propertyMappings ?? []}
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((scope) => {
|
||||
let selected = false;
|
||||
if (!provider?.propertyMappings) {
|
||||
selected = scope.managed
|
||||
? defaultScopes.includes(scope.managed)
|
||||
: false;
|
||||
} else {
|
||||
selected = Array.from(provider?.propertyMappings).some(
|
||||
(su) => {
|
||||
return su == scope.pk;
|
||||
},
|
||||
);
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(scope.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${scope.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${oauth2PropertyMappingsProvider}
|
||||
.selector=${makeOAuth2PropertyMappingsSelector(
|
||||
provider?.propertyMappings,
|
||||
)}
|
||||
available-label=${msg("Available Scopes")}
|
||||
selected-label=${msg("Selected Scopes")}
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Select which scopes can be used by the client. The client still has to specify the scope to access the data.",
|
||||
)}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-radio-input
|
||||
|
@ -1,10 +1,15 @@
|
||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||
import {
|
||||
makeProxyPropertyMappingsSelector,
|
||||
proxyPropertyMappingsProvider,
|
||||
} from "@goauthentik/admin/providers/proxy/ProxyProviderPropertyMappings.js";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
import "@goauthentik/components/ak-toggle-group";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -16,7 +21,6 @@ import {
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PaginatedOAuthSourceList,
|
||||
PaginatedScopeMappingList,
|
||||
PropertymappingsApi,
|
||||
ProxyMode,
|
||||
ProxyProvider,
|
||||
SourcesApi,
|
||||
@ -29,12 +33,6 @@ type MaybeTemplateResult = TemplateResult | typeof nothing;
|
||||
export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||
constructor() {
|
||||
super();
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScopeList({ ordering: "scope_name" })
|
||||
.then((propertyMappings: PaginatedScopeMappingList) => {
|
||||
this.propertyMappings = propertyMappings;
|
||||
});
|
||||
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthList({
|
||||
ordering: "name",
|
||||
@ -88,29 +86,8 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||
</ak-text-input>`;
|
||||
}
|
||||
|
||||
scopeMappingConfiguration(provider?: ProxyProvider) {
|
||||
const propertyMappings = this.propertyMappings?.results ?? [];
|
||||
|
||||
const defaultScopes = () =>
|
||||
propertyMappings
|
||||
.filter((scope) => !(scope?.managed ?? "").startsWith("goauthentik.io/providers"))
|
||||
.map((pm) => pm.pk);
|
||||
|
||||
const configuredScopes = (providerMappings: string[]) =>
|
||||
propertyMappings.map((scope) => scope.pk).filter((pk) => providerMappings.includes(pk));
|
||||
|
||||
const scopeValues = provider?.propertyMappings
|
||||
? configuredScopes(provider?.propertyMappings ?? [])
|
||||
: defaultScopes();
|
||||
|
||||
const scopePairs = propertyMappings.map((scope) => [scope.pk, scope.name]);
|
||||
|
||||
return { scopePairs, scopeValues };
|
||||
}
|
||||
|
||||
render() {
|
||||
const errors = this.wizard.errors.provider;
|
||||
const { scopePairs, scopeValues } = this.scopeMappingConfiguration(this.instance);
|
||||
|
||||
return html` <ak-wizard-title>${msg("Configure Proxy Provider")}</ak-wizard-title>
|
||||
<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||
@ -179,24 +156,22 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||
certificate=${ifDefined(this.instance?.certificate ?? undefined)}
|
||||
></ak-crypto-certificate-search>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-multi-select
|
||||
label=${msg("AdditionalScopes")}
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Additional scopes")}
|
||||
name="propertyMappings"
|
||||
.options=${scopePairs}
|
||||
.values=${scopeValues}
|
||||
.errorMessages=${errors?.propertyMappings ?? []}
|
||||
.richhelp=${html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Additional scope mappings, which are passed to the proxy.",
|
||||
)}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
`}
|
||||
></ak-multi-select>
|
||||
>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${proxyPropertyMappingsProvider}
|
||||
.selector=${makeProxyPropertyMappingsSelector(
|
||||
this.instance?.propertyMappings,
|
||||
)}
|
||||
available-label="${msg("Available Scopes")}"
|
||||
selected-label="${msg("Selected Scopes")}"
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Additional scope mappings, which are passed to the proxy.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-textarea-input
|
||||
name="skipPathRegex"
|
||||
|
@ -1,44 +1,28 @@
|
||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import {
|
||||
makeRACPropertyMappingsSelector,
|
||||
racPropertyMappingsProvider,
|
||||
} from "@goauthentik/admin/providers/rac/RACPropertyMappings.js";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PaginatedRACPropertyMappingList,
|
||||
PropertymappingsApi,
|
||||
RACProvider,
|
||||
} from "@goauthentik/api";
|
||||
import { FlowsInstancesListDesignationEnum, RACProvider } from "@goauthentik/api";
|
||||
|
||||
import BaseProviderPanel from "../BaseProviderPanel";
|
||||
|
||||
@customElement("ak-application-wizard-authentication-for-rac")
|
||||
export class ApplicationWizardAuthenticationByRAC extends BaseProviderPanel {
|
||||
@state()
|
||||
propertyMappings?: PaginatedRACPropertyMappingList;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsRacList({
|
||||
ordering: "name",
|
||||
})
|
||||
.then((propertyMappings) => {
|
||||
this.propertyMappings = propertyMappings;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const provider = this.wizard.provider as RACProvider | undefined;
|
||||
const selected = new Set(Array.from(provider?.propertyMappings ?? []));
|
||||
const errors = this.wizard.errors.provider;
|
||||
|
||||
return html`<ak-wizard-title
|
||||
@ -85,17 +69,14 @@ export class ApplicationWizardAuthenticationByRAC extends BaseProviderPanel {
|
||||
label=${msg("Property mappings")}
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map(
|
||||
(mapping) =>
|
||||
html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected.has(mapping.pk)}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`,
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${racPropertyMappingsProvider}
|
||||
.selector=${makeRACPropertyMappingsSelector(
|
||||
provider?.propertyMappings,
|
||||
)}
|
||||
</select>
|
||||
available-label="${msg("Available Property Mappings")}"
|
||||
selected-label="${msg("Selected Property Mappings")}"
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
|
@ -6,6 +6,7 @@ import { ascii_letters, digits, first, randomString } from "@goauthentik/common/
|
||||
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/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
@ -23,13 +24,16 @@ import {
|
||||
IssuerModeEnum,
|
||||
OAuth2Provider,
|
||||
PaginatedOAuthSourceList,
|
||||
PaginatedScopeMappingList,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
SourcesApi,
|
||||
SubModeEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import {
|
||||
makeOAuth2PropertyMappingsSelector,
|
||||
oauth2PropertyMappingsProvider,
|
||||
} from "./Oauth2PropertyMappings.js";
|
||||
|
||||
export const clientTypeOptions = [
|
||||
{
|
||||
label: msg("Confidential"),
|
||||
@ -123,7 +127,6 @@ export const redirectUriHelp = html`${redirectUriHelpMessages.map(
|
||||
|
||||
@customElement("ak-provider-oauth2-form")
|
||||
export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
|
||||
propertyMappings?: PaginatedScopeMappingList;
|
||||
oauthSources?: PaginatedOAuthSourceList;
|
||||
|
||||
@state()
|
||||
@ -138,11 +141,6 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsScopeList({
|
||||
ordering: "scope_name",
|
||||
});
|
||||
this.oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({
|
||||
ordering: "name",
|
||||
hasJwks: true,
|
||||
@ -291,34 +289,20 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
|
||||
>
|
||||
</ak-text-input>
|
||||
<ak-form-element-horizontal label=${msg("Scopes")} name="propertyMappings">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((scope) => {
|
||||
let selected = false;
|
||||
if (!provider?.propertyMappings) {
|
||||
selected = scope.managed
|
||||
? defaultScopes.includes(scope.managed)
|
||||
: false;
|
||||
} else {
|
||||
selected = Array.from(provider?.propertyMappings).some((su) => {
|
||||
return su == scope.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(scope.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${scope.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${oauth2PropertyMappingsProvider}
|
||||
.selector=${makeOAuth2PropertyMappingsSelector(
|
||||
provider?.propertyMappings,
|
||||
)}
|
||||
available-label=${msg("Available Scopes")}
|
||||
selected-label=${msg("Selected Scopes")}
|
||||
></ak-dual-select-dynamic-selected>
|
||||
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Select which scopes can be used by the client. The client still has to specify the scope to access the data.",
|
||||
)}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-radio-input
|
||||
name="subMode"
|
||||
|
28
web/src/admin/providers/oauth2/Oauth2PropertyMappings.ts
Normal file
28
web/src/admin/providers/oauth2/Oauth2PropertyMappings.ts
Normal file
@ -0,0 +1,28 @@
|
||||
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 oauth2PropertyMappingsProvider(page = 1, search = "") {
|
||||
const propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsScopeList({
|
||||
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<ScopeMapping>) =>
|
||||
scope?.managed?.startsWith("goauthentik.io/providers/oauth2/scope-") &&
|
||||
scope?.managed !== "goauthentik.io/providers/oauth2/scope-offline_access";
|
||||
}
|
@ -4,6 +4,7 @@ import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm"
|
||||
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/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
@ -21,14 +22,17 @@ import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";
|
||||
import {
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PaginatedOAuthSourceList,
|
||||
PaginatedScopeMappingList,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
ProxyMode,
|
||||
ProxyProvider,
|
||||
SourcesApi,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import {
|
||||
makeProxyPropertyMappingsSelector,
|
||||
proxyPropertyMappingsProvider,
|
||||
} from "./ProxyProviderPropertyMappings.js";
|
||||
|
||||
@customElement("ak-provider-proxy-form")
|
||||
export class ProxyProviderFormPage extends BaseProviderForm<ProxyProvider> {
|
||||
static get styles(): CSSResult[] {
|
||||
@ -45,18 +49,12 @@ export class ProxyProviderFormPage extends BaseProviderForm<ProxyProvider> {
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsScopeList({
|
||||
ordering: "scope_name",
|
||||
});
|
||||
this.oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({
|
||||
ordering: "name",
|
||||
hasJwks: true,
|
||||
});
|
||||
}
|
||||
|
||||
propertyMappings?: PaginatedScopeMappingList;
|
||||
oauthSources?: PaginatedOAuthSourceList;
|
||||
|
||||
@state()
|
||||
@ -323,31 +321,17 @@ export class ProxyProviderFormPage extends BaseProviderForm<ProxyProvider> {
|
||||
label=${msg("Additional scopes")}
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results
|
||||
.filter((scope) => {
|
||||
return !scope.managed?.startsWith("goauthentik.io/providers");
|
||||
})
|
||||
.map((scope) => {
|
||||
const selected = (this.instance?.propertyMappings || []).some(
|
||||
(su) => {
|
||||
return su == scope.pk;
|
||||
},
|
||||
);
|
||||
return html`<option
|
||||
value=${ifDefined(scope.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${scope.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${proxyPropertyMappingsProvider}
|
||||
.selector=${makeProxyPropertyMappingsSelector(
|
||||
this.instance?.propertyMappings,
|
||||
)}
|
||||
available-label="${msg("Available Scopes")}"
|
||||
selected-label="${msg("Selected Scopes")}"
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Additional scope mappings, which are passed to the proxy.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-element-horizontal
|
||||
|
@ -0,0 +1,27 @@
|
||||
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,
|
||||
).propertymappingsScopeList({
|
||||
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<ScopeMapping>) =>
|
||||
!(scope?.managed ?? "").startsWith("goauthentik.io/providers");
|
||||
}
|
@ -2,6 +2,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
@ -12,30 +13,18 @@ import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import { AuthModeEnum, Endpoint, ProtocolEnum, RacApi } from "@goauthentik/api";
|
||||
|
||||
import {
|
||||
AuthModeEnum,
|
||||
Endpoint,
|
||||
PaginatedRACPropertyMappingList,
|
||||
PropertymappingsApi,
|
||||
ProtocolEnum,
|
||||
RacApi,
|
||||
} from "@goauthentik/api";
|
||||
makeRACPropertyMappingsSelector,
|
||||
racPropertyMappingsProvider,
|
||||
} from "./RACPropertyMappings.js";
|
||||
|
||||
@customElement("ak-rac-endpoint-form")
|
||||
export class EndpointForm extends ModelForm<Endpoint, string> {
|
||||
@property({ type: Number })
|
||||
providerID?: number;
|
||||
|
||||
propertyMappings?: PaginatedRACPropertyMappingList;
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsRacList({
|
||||
ordering: "name",
|
||||
});
|
||||
}
|
||||
|
||||
loadInstance(pk: string): Promise<Endpoint> {
|
||||
return new RacApi(DEFAULT_CONFIG).racEndpointsRetrieve({
|
||||
pbmUuid: pk,
|
||||
@ -124,22 +113,12 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Property mappings")} name="propertyMappings">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (this.instance?.propertyMappings) {
|
||||
selected = Array.from(this.instance?.propertyMappings).some((su) => {
|
||||
return su == mapping.pk;
|
||||
});
|
||||
}
|
||||
return html`<option value=${ifDefined(mapping.pk)} ?selected=${selected}>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${racPropertyMappingsProvider}
|
||||
.selector=${makeRACPropertyMappingsSelector(this.instance?.propertyMappings)}
|
||||
available-label="${msg("Available User Property Mappings")}"
|
||||
selected-label="${msg("Selected User Property Mappings")}"
|
||||
></ak-dual-select-dynamic-selected>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group>
|
||||
<span slot="header"> ${msg("Advanced settings")} </span>
|
||||
|
22
web/src/admin/providers/rac/RACPropertyMappings.ts
Normal file
22
web/src/admin/providers/rac/RACPropertyMappings.ts
Normal file
@ -0,0 +1,22 @@
|
||||
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).propertymappingsRacList({
|
||||
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);
|
||||
}
|
@ -3,6 +3,7 @@ import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
@ -13,30 +14,18 @@ import YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import { FlowsInstancesListDesignationEnum, ProvidersApi, RACProvider } from "@goauthentik/api";
|
||||
|
||||
import {
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PaginatedRACPropertyMappingList,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
RACProvider,
|
||||
} from "@goauthentik/api";
|
||||
makeRACPropertyMappingsSelector,
|
||||
racPropertyMappingsProvider,
|
||||
} from "./RACPropertyMappings.js";
|
||||
|
||||
@customElement("ak-provider-rac-form")
|
||||
export class RACProviderFormPage extends ModelForm<RACProvider, number> {
|
||||
@state()
|
||||
propertyMappings?: PaginatedRACPropertyMappingList;
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsRacList({
|
||||
ordering: "name",
|
||||
});
|
||||
}
|
||||
|
||||
async loadInstance(pk: number): Promise<RACProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersRacRetrieve({
|
||||
id: pk,
|
||||
@ -137,27 +126,14 @@ export class RACProviderFormPage extends ModelForm<RACProvider, number> {
|
||||
label=${msg("Property mappings")}
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (this.instance?.propertyMappings) {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(
|
||||
(su) => {
|
||||
return su == mapping.pk;
|
||||
},
|
||||
);
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${racPropertyMappingsProvider}
|
||||
.selector=${makeRACPropertyMappingsSelector(
|
||||
this.instance?.propertyMappings,
|
||||
)}
|
||||
available-label="${msg("Available Property Mappings")}"
|
||||
selected-label="${msg("Selected Property Mappings")}"
|
||||
></ak-dual-select-dynamic-selected>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Settings")} name="settings">
|
||||
<ak-codemirror
|
||||
|
@ -2,6 +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 { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
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";
|
||||
@ -16,7 +18,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import {
|
||||
DigestAlgorithmEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PaginatedSAMLPropertyMappingList,
|
||||
PropertymappingsApi,
|
||||
PropertymappingsSamlListRequest,
|
||||
ProvidersApi,
|
||||
@ -26,6 +27,29 @@ import {
|
||||
SpBindingEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
export async function samlPropertyMappingsProvider(page = 1, search = "") {
|
||||
const propertyMappings = await new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSamlList(
|
||||
{
|
||||
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<SAMLPropertyMapping>) =>
|
||||
mapping?.managed?.startsWith("goauthentik.io/providers/saml");
|
||||
}
|
||||
|
||||
@customElement("ak-provider-saml-form")
|
||||
export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
|
||||
loadInstance(pk: number): Promise<SAMLProvider> {
|
||||
@ -34,16 +58,6 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
|
||||
});
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsSamlList({
|
||||
ordering: "saml_name",
|
||||
});
|
||||
}
|
||||
|
||||
propertyMappings?: PaginatedSAMLPropertyMappingList;
|
||||
|
||||
async send(data: SAMLProvider): Promise<SAMLProvider> {
|
||||
if (this.instance) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersSamlUpdate({
|
||||
@ -193,29 +207,14 @@ export class SAMLProviderFormPage extends BaseProviderForm<SAMLProvider> {
|
||||
label=${msg("Property mappings")}
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
mapping.managed?.startsWith(
|
||||
"goauthentik.io/providers/saml",
|
||||
) || false;
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(
|
||||
(su) => {
|
||||
return su == mapping.pk;
|
||||
},
|
||||
);
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${samlPropertyMappingsProvider}
|
||||
.selector=${makeSAMLPropertyMappingsSelector(
|
||||
this.instance?.propertyMappings,
|
||||
)}
|
||||
available-label=${msg("Available User Property Mappings")}
|
||||
selected-label=${msg("Selected User Property Mappings")}
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
|
@ -1,6 +1,8 @@
|
||||
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";
|
||||
@ -15,12 +17,35 @@ import {
|
||||
CoreApi,
|
||||
CoreGroupsListRequest,
|
||||
Group,
|
||||
PaginatedSCIMMappingList,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
SCIMMapping,
|
||||
SCIMProvider,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
export async function scimPropertyMappingsProvider(page = 1, search = "") {
|
||||
const propertyMappings = await new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsScimList(
|
||||
{
|
||||
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) {
|
||||
const localMappings = instanceMappings ? new Set(instanceMappings) : undefined;
|
||||
return localMappings
|
||||
? ([pk, _]: DualSelectPair) => localMappings.has(pk)
|
||||
: ([_0, _1, _2, mapping]: DualSelectPair<SCIMMapping>) =>
|
||||
mapping?.managed === "goauthentik.io/providers/scim/user";
|
||||
}
|
||||
|
||||
@customElement("ak-provider-scim-form")
|
||||
export class SCIMProviderFormPage extends BaseProviderForm<SCIMProvider> {
|
||||
loadInstance(pk: number): Promise<SCIMProvider> {
|
||||
@ -29,16 +54,6 @@ export class SCIMProviderFormPage extends BaseProviderForm<SCIMProvider> {
|
||||
});
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsScimList({
|
||||
ordering: "managed",
|
||||
});
|
||||
}
|
||||
|
||||
propertyMappings?: PaginatedSCIMMappingList;
|
||||
|
||||
async send(data: SCIMProvider): Promise<SCIMProvider> {
|
||||
if (this.instance) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersScimUpdate({
|
||||
@ -152,68 +167,34 @@ export class SCIMProviderFormPage extends BaseProviderForm<SCIMProvider> {
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("User Property Mappings")}
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
mapping.managed === "goauthentik.io/providers/scim/user" ||
|
||||
false;
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(
|
||||
(su) => {
|
||||
return su == mapping.pk;
|
||||
},
|
||||
);
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
name="propertyMappings">
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${scimPropertyMappingsProvider}
|
||||
.selector=${makeSCIMPropertyMappingsSelector(
|
||||
this.instance?.propertyMappings,
|
||||
)}
|
||||
available-label=${msg("Available User Property Mappings")}
|
||||
selected-label=${msg("Selected User Property Mappings")}
|
||||
></ak-dual-select-dynamic-selected>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Property mappings used to user mapping.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Group Property Mappings")}
|
||||
name="propertyMappingsGroup"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappingsGroup) {
|
||||
selected =
|
||||
mapping.managed === "goauthentik.io/providers/scim/group";
|
||||
} else {
|
||||
selected = Array.from(
|
||||
this.instance?.propertyMappingsGroup,
|
||||
).some((su) => {
|
||||
return su == mapping.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
name="propertyMappingsGroup">
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${scimPropertyMappingsProvider}
|
||||
.selector=${makeSCIMPropertyMappingsSelector(
|
||||
this.instance?.propertyMappingsGroup,
|
||||
)}
|
||||
available-label=${msg("Available Group Property Mappings")}
|
||||
selected-label=${msg("Selected Group Property Mappings")}
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Property mappings used to group creation.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
|
@ -3,6 +3,8 @@ import { placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
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";
|
||||
@ -16,13 +18,37 @@ import {
|
||||
CoreApi,
|
||||
CoreGroupsListRequest,
|
||||
Group,
|
||||
LDAPPropertyMapping,
|
||||
LDAPSource,
|
||||
LDAPSourceRequest,
|
||||
PaginatedLDAPPropertyMappingList,
|
||||
PropertymappingsApi,
|
||||
SourcesApi,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
async function propertyMappingsProvider(page = 1, search = "") {
|
||||
const propertyMappings = await new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsLdapList(
|
||||
{
|
||||
ordering: "managed,object_field",
|
||||
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<LDAPPropertyMapping>) =>
|
||||
mapping?.managed?.startsWith("goauthentik.io/sources/ldap/default") ||
|
||||
mapping?.managed?.startsWith("goauthentik.io/sources/ldap/ms");
|
||||
}
|
||||
|
||||
@customElement("ak-source-ldap-form")
|
||||
export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
||||
loadInstance(pk: string): Promise<LDAPSource> {
|
||||
@ -31,16 +57,6 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
||||
});
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsLdapList({
|
||||
ordering: "managed,object_field",
|
||||
});
|
||||
}
|
||||
|
||||
propertyMappings?: PaginatedLDAPPropertyMappingList;
|
||||
|
||||
async send(data: LDAPSource): Promise<LDAPSource> {
|
||||
if (this.instance) {
|
||||
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapPartialUpdate({
|
||||
@ -277,71 +293,32 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
||||
label=${msg("User Property Mappings")}
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
mapping.managed?.startsWith(
|
||||
"goauthentik.io/sources/ldap/default",
|
||||
) ||
|
||||
mapping.managed?.startsWith(
|
||||
"goauthentik.io/sources/ldap/ms",
|
||||
) ||
|
||||
false;
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(
|
||||
(su) => {
|
||||
return su == mapping.pk;
|
||||
},
|
||||
);
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${propertyMappingsProvider}
|
||||
.selector=${makePropertyMappingsSelector(
|
||||
this.instance?.propertyMappings,
|
||||
)}
|
||||
available-label="${msg("Available User Property Mappings")}"
|
||||
selected-label="${msg("Selected User Property Mappings")}"
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Property mappings used to user creation.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
${msg("Property mappings for user creation.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Group Property Mappings")}
|
||||
name="propertyMappingsGroup"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappingsGroup) {
|
||||
selected =
|
||||
mapping.managed ===
|
||||
"goauthentik.io/sources/ldap/default-name";
|
||||
} else {
|
||||
selected = Array.from(
|
||||
this.instance?.propertyMappingsGroup,
|
||||
).some((su) => {
|
||||
return su == mapping.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<ak-dual-select-dynamic-selected
|
||||
.provider=${propertyMappingsProvider}
|
||||
.selector=${makePropertyMappingsSelector(
|
||||
this.instance?.propertyMappingsGroup,
|
||||
)}
|
||||
available-label="${msg("Available Group Property Mappings")}"
|
||||
selected-label="${msg("Selected Group Property Mappings")}"
|
||||
></ak-dual-select-dynamic-selected>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Property mappings used to group creation.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
${msg("Property mappings for group creation.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
|
@ -0,0 +1,52 @@
|
||||
import { PropertyValues, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ref } from "lit/directives/ref.js";
|
||||
|
||||
import { AkDualSelectProvider } from "./ak-dual-select-provider.js";
|
||||
import "./ak-dual-select.js";
|
||||
import type { DualSelectPair } from "./types.js";
|
||||
|
||||
/**
|
||||
* @element ak-dual-select-dynamic-provider
|
||||
*
|
||||
* A top-level component for multi-select elements have dynamically generated "selected"
|
||||
* lists.
|
||||
*/
|
||||
|
||||
@customElement("ak-dual-select-dynamic-selected")
|
||||
export class AkDualSelectDynamic extends AkDualSelectProvider {
|
||||
/**
|
||||
* An extra source of "default" entries. A number of our collections have an alternative default
|
||||
* source when initializing a new component instance of that collection's host object. Only run
|
||||
* on start-up.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ attribute: false })
|
||||
selector: ([key, _]: DualSelectPair) => boolean = ([_key, _]) => false;
|
||||
|
||||
private firstUpdateHasRun = false;
|
||||
|
||||
willUpdate(changed: PropertyValues<this>) {
|
||||
super.willUpdate(changed);
|
||||
// On the first update *only*, even before rendering, when the options are handed up, update
|
||||
// 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)]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<ak-dual-select
|
||||
${ref(this.dualSelector)}
|
||||
.options=${this.options}
|
||||
.pages=${this.pagination}
|
||||
.selected=${this.selected}
|
||||
available-label=${this.availableLabel}
|
||||
selected-label=${this.selectedLabel}
|
||||
></ak-dual-select>`;
|
||||
}
|
||||
}
|
@ -27,36 +27,59 @@ import type { DataProvider, DualSelectPair } from "./types";
|
||||
|
||||
@customElement("ak-dual-select-provider")
|
||||
export class AkDualSelectProvider extends CustomListenerElement(AKElement) {
|
||||
/** A function that takes a page and returns the DualSelectPair[] collection with which to update
|
||||
/**
|
||||
* A function that takes a page and returns the DualSelectPair[] collection with which to update
|
||||
* the "Available" pane.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Object })
|
||||
provider!: DataProvider;
|
||||
|
||||
/**
|
||||
* The list of selected items. This is the *complete* list, not paginated, as presented by a
|
||||
* component with a multi-select list of items to track.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Array })
|
||||
selected: DualSelectPair[] = [];
|
||||
|
||||
/**
|
||||
* The label for the left ("available") pane
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ attribute: "available-label" })
|
||||
availableLabel = msg("Available options");
|
||||
|
||||
/**
|
||||
* The label for the right ("selected") pane
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ attribute: "selected-label" })
|
||||
selectedLabel = msg("Selected options");
|
||||
|
||||
/** The remote lists are debounced by definition. This is the interval for the debounce. */
|
||||
/**
|
||||
* The debounce for the search as the user is typing in a request
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ attribute: "search-delay", type: Number })
|
||||
searchDelay = 250;
|
||||
|
||||
@state()
|
||||
private options: DualSelectPair[] = [];
|
||||
options: DualSelectPair[] = [];
|
||||
|
||||
private dualSelector: Ref<AkDualSelect> = createRef();
|
||||
protected dualSelector: Ref<AkDualSelect> = createRef();
|
||||
|
||||
private isLoading = false;
|
||||
protected isLoading = false;
|
||||
|
||||
private doneFirstUpdate = false;
|
||||
private internalSelected: DualSelectPair[] = [];
|
||||
|
||||
private pagination?: Pagination;
|
||||
protected pagination?: Pagination;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -4,7 +4,7 @@ import { Pagination } from "@goauthentik/api";
|
||||
|
||||
// Key, Label (string or TemplateResult), (optional) string to sort by. If the sort string is
|
||||
// missing, it will use the label, which doesn't always work for TemplateResults).
|
||||
export type DualSelectPair = [string, string | TemplateResult, string?];
|
||||
export type DualSelectPair<T = never> = [string, string | TemplateResult, string?, T?];
|
||||
|
||||
export type BasePagination = Pick<
|
||||
Pagination,
|
||||
|
Reference in New Issue
Block a user