web/elements: only render form once instance is loaded (#5049)
* web/elements: only render form once instance is loaded Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use radio for transport Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only wait for instance to be loaded if set Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add hook to load additional data in form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make send an abstract function instead of attribute Signed-off-by: Jens Langhammer <jens@goauthentik.io> * ensure form is updated after data is loaded Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove until for select and multi-selects in forms Signed-off-by: Jens Langhammer <jens@goauthentik.io> * don't use until for file uploads Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove last until from form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove deprecated import Signed-off-by: Jens Langhammer <jens@goauthentik.io> * prevent form double load, add error handling for PreventFormSubmit Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix double creation of inner element in proxy form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make PreventFormSubmit work correctly Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -11,9 +11,8 @@ import "@goauthentik/elements/utils/TimeDeltaHelp";
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import {
|
||||
CertificateKeyPair,
|
||||
@ -26,6 +25,8 @@ import {
|
||||
FlowsInstancesListRequest,
|
||||
IssuerModeEnum,
|
||||
OAuth2Provider,
|
||||
PaginatedOAuthSourceList,
|
||||
PaginatedScopeMappingList,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
SourcesApi,
|
||||
@ -34,19 +35,31 @@ import {
|
||||
|
||||
@customElement("ak-provider-oauth2-form")
|
||||
export class OAuth2ProviderFormPage extends ModelForm<OAuth2Provider, number> {
|
||||
loadInstance(pk: number): Promise<OAuth2Provider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersOauth2Retrieve({
|
||||
id: pk,
|
||||
})
|
||||
.then((provider) => {
|
||||
this.showClientSecret = provider.clientType === ClientTypeEnum.Confidential;
|
||||
return provider;
|
||||
});
|
||||
propertyMappings?: PaginatedScopeMappingList;
|
||||
oauthSources?: PaginatedOAuthSourceList;
|
||||
|
||||
@state()
|
||||
showClientSecret = true;
|
||||
|
||||
async loadInstance(pk: number): Promise<OAuth2Provider> {
|
||||
const provider = await new ProvidersApi(DEFAULT_CONFIG).providersOauth2Retrieve({
|
||||
id: pk,
|
||||
});
|
||||
this.showClientSecret = provider.clientType === ClientTypeEnum.Confidential;
|
||||
return provider;
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
showClientSecret = true;
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
@ -287,36 +300,27 @@ ${this.instance?.redirectUris}</textarea
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Scopes`} name="propertyMappings">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScopeList({
|
||||
ordering: "scope_name",
|
||||
})
|
||||
.then((scopes) => {
|
||||
return scopes.results.map((scope) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
scope.managed?.startsWith(
|
||||
"goauthentik.io/providers/oauth2/scope-",
|
||||
) || false;
|
||||
} else {
|
||||
selected = Array.from(
|
||||
this.instance?.propertyMappings,
|
||||
).some((su) => {
|
||||
return su == scope.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(scope.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${scope.name}
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
${this.propertyMappings?.results.map((scope) => {
|
||||
let selected = false;
|
||||
if (!this.instance?.propertyMappings) {
|
||||
selected =
|
||||
scope.managed?.startsWith(
|
||||
"goauthentik.io/providers/oauth2/scope-",
|
||||
) || false;
|
||||
} else {
|
||||
selected = Array.from(this.instance?.propertyMappings).some(
|
||||
(su) => {
|
||||
return su == scope.pk;
|
||||
},
|
||||
);
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(scope.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${scope.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Select which scopes can be used by the client. The client still has to specify the scope to access the data.`}
|
||||
@ -413,29 +417,14 @@ ${this.instance?.redirectUris}</textarea
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal label=${t`Trusted OIDC Sources`} name="jwksSources">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthList({
|
||||
ordering: "name",
|
||||
hasJwks: true,
|
||||
})
|
||||
.then((sources) => {
|
||||
return sources.results.map((source) => {
|
||||
const selected = (
|
||||
this.instance?.jwksSources || []
|
||||
).some((su) => {
|
||||
return su == source.pk;
|
||||
});
|
||||
return html`<option
|
||||
value=${source.pk}
|
||||
?selected=${selected}
|
||||
>
|
||||
${source.name} (${source.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
${this.oauthSources?.results.map((source) => {
|
||||
const selected = (this.instance?.jwksSources || []).some((su) => {
|
||||
return su == source.pk;
|
||||
});
|
||||
return html`<option value=${source.pk} ?selected=${selected}>
|
||||
${source.name} (${source.slug})
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.`}
|
||||
|
||||
@ -13,7 +13,6 @@ import { CSSResult, css } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||
@ -28,6 +27,8 @@ import {
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
FlowsInstancesListRequest,
|
||||
PaginatedOAuthSourceList,
|
||||
PaginatedScopeMappingList,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
ProxyMode,
|
||||
@ -51,18 +52,30 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
);
|
||||
}
|
||||
|
||||
loadInstance(pk: number): Promise<ProxyProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG)
|
||||
.providersProxyRetrieve({
|
||||
id: pk,
|
||||
})
|
||||
.then((provider) => {
|
||||
this.showHttpBasic = first(provider.basicAuthEnabled, true);
|
||||
this.mode = first(provider.mode, ProxyMode.Proxy);
|
||||
return provider;
|
||||
});
|
||||
async loadInstance(pk: number): Promise<ProxyProvider> {
|
||||
const provider = await new ProvidersApi(DEFAULT_CONFIG).providersProxyRetrieve({
|
||||
id: pk,
|
||||
});
|
||||
this.showHttpBasic = first(provider.basicAuthEnabled, true);
|
||||
this.mode = first(provider.mode, ProxyMode.Proxy);
|
||||
return provider;
|
||||
}
|
||||
|
||||
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()
|
||||
showHttpBasic = true;
|
||||
|
||||
@ -392,34 +405,23 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScopeList({
|
||||
ordering: "scope_name",
|
||||
})
|
||||
.then((scopes) => {
|
||||
return scopes.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>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
${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>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Additional scope mappings, which are passed to the proxy.`}
|
||||
@ -497,29 +499,14 @@ ${this.instance?.skipPathRegex}</textarea
|
||||
${this.showHttpBasic ? this.renderHttpBasic() : html``}
|
||||
<ak-form-element-horizontal label=${t`Trusted OIDC Sources`} name="jwksSources">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthList({
|
||||
ordering: "name",
|
||||
hasJwks: true,
|
||||
})
|
||||
.then((sources) => {
|
||||
return sources.results.map((source) => {
|
||||
const selected = (
|
||||
this.instance?.jwksSources || []
|
||||
).some((su) => {
|
||||
return su == source.pk;
|
||||
});
|
||||
return html`<option
|
||||
value=${source.pk}
|
||||
?selected=${selected}
|
||||
>
|
||||
${source.name} (${source.slug})
|
||||
</option>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
${this.oauthSources?.results.map((source) => {
|
||||
const selected = (this.instance?.jwksSources || []).some((su) => {
|
||||
return su == source.pk;
|
||||
});
|
||||
return html`<option value=${source.pk} ?selected=${selected}>
|
||||
${source.name} (${source.slug})
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.`}
|
||||
|
||||
@ -9,9 +9,9 @@ import "@goauthentik/elements/forms/SearchSelect";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { customElement } from "lit-element";
|
||||
import { TemplateResult, html } from "lit-html";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { ifDefined } from "lit-html/directives/if-defined.js";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import {
|
||||
Flow,
|
||||
|
||||
@ -11,7 +11,8 @@ import "@goauthentik/elements/events/ObjectChangelog";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, TemplateResult, customElement, html, property } from "lit-element";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
|
||||
@ -12,7 +12,6 @@ import { t } from "@lingui/macro";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import {
|
||||
CertificateKeyPair,
|
||||
@ -23,6 +22,7 @@ import {
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
FlowsInstancesListRequest,
|
||||
PaginatedSAMLPropertyMappingList,
|
||||
PropertymappingsApi,
|
||||
PropertymappingsSamlListRequest,
|
||||
ProvidersApi,
|
||||
@ -40,6 +40,16 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
||||
});
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsSamlList({
|
||||
ordering: "saml_name",
|
||||
});
|
||||
}
|
||||
|
||||
propertyMappings?: PaginatedSAMLPropertyMappingList;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
return t`Successfully updated provider.`;
|
||||
@ -241,36 +251,27 @@ export class SAMLProviderFormPage extends ModelForm<SAMLProvider, number> {
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsSamlList({
|
||||
ordering: "saml_name",
|
||||
})
|
||||
.then((mappings) => {
|
||||
return mappings.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>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
${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>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Hold control/command to select multiple items.`}
|
||||
|
||||
@ -11,12 +11,12 @@ import { t } from "@lingui/macro";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
import {
|
||||
CoreApi,
|
||||
CoreGroupsListRequest,
|
||||
Group,
|
||||
PaginatedSCIMMappingList,
|
||||
PropertymappingsApi,
|
||||
ProvidersApi,
|
||||
SCIMProvider,
|
||||
@ -30,6 +30,16 @@ export class SCIMProviderFormPage extends ModelForm<SCIMProvider, number> {
|
||||
});
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.propertyMappings = await new PropertymappingsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).propertymappingsScimList({
|
||||
ordering: "managed",
|
||||
});
|
||||
}
|
||||
|
||||
propertyMappings?: PaginatedSCIMMappingList;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
return t`Successfully updated provider.`;
|
||||
@ -147,36 +157,26 @@ export class SCIMProviderFormPage extends ModelForm<SCIMProvider, number> {
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScimList({
|
||||
ordering: "managed",
|
||||
})
|
||||
.then((mappings) => {
|
||||
return mappings.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>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
${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>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Property mappings used to user mapping.`}
|
||||
@ -191,35 +191,25 @@ export class SCIMProviderFormPage extends ModelForm<SCIMProvider, number> {
|
||||
name="propertyMappingsGroup"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${until(
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScimList({
|
||||
ordering: "managed",
|
||||
})
|
||||
.then((mappings) => {
|
||||
return mappings.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>`;
|
||||
});
|
||||
}),
|
||||
html`<option>${t`Loading...`}</option>`,
|
||||
)}
|
||||
${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>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Property mappings used to group creation.`}
|
||||
|
||||
Reference in New Issue
Block a user