web: abstract rootInterface()?.config?.capabilities.includes() into .can() (#7737)
* This commit abstracts access to the object `rootInterface()?.config?` into a single accessor,
`authentikConfig`, that can be mixed into any AKElement object that requires access to it.
Since access to `rootInterface()?.config?` is _universally_ used for a single (and repetitive)
boolean check, a separate accessor has been provided that converts all calls of the form:
``` javascript
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate)
```
into:
``` javascript
this.can(CapabilitiesEnum.CanImpersonate)
```
It does this via a Mixin, `WithCapabilitiesConfig`, which understands that these calls only make
sense in the context of a running, fully configured authentik instance, and that their purpose is to
inform authentik components of a user’s capabilities. The latter is why I don’t feel uncomfortable
turning a function call into a method; we should make it explicit that this is a relationship
between components.
The mixin has a single single field, `[WCC.capabilitiesConfig]`, where its association with the
upper-level configuration is made. If that syntax looks peculiar to you, good! I’ve used an explict
unique symbol as the field name; it is inaccessable an innumerable in the object list. The debugger
shows it only as:
Symbol(): {
cacheTimeout: 300
cacheTimeoutFlows: 300
cacheTimeoutPolicies: 300
cacheTimeoutReputation: 300
capabilities: (5) ['can_save_media', 'can_geo_ip', 'can_impersonate', 'can_debug', 'is_enterprise']
}
Since you can’t reference it by identity, you can’t write to it. Until every browser supports actual
private fields, this is the best we can do; it does guarantee that field name collisions are
impossible, which is a win.
The mixin takes a second optional boolean; setting this to true will cause any web component using
the mixin to automatically schedule a re-render if the capabilities list changes.
The mixin is also generic; despite the "...into a Lit-Context" in the title, the internals of the
Mixin can be replaced with anything so long as the signature of `.can()` is preserved.
Because this work builds off the work I did to give the Sidebar access to the configuration without
ad-hoc retrieval or prop-drilling, it wasn’t necessary to create a new context for it. That will be
necessary for the following:
TODO:
``` javascript
rootInterface()?.uiConfig;
rootInterface()?.tenant;
me();
```
* web: Added a README with a description of the applications' "mental model," essentially an architectural description.
* web: prettier had opinions about the README
* web: Jens requested that subscription be by default, and it's the right call.
* This commit abstracts access to the object `rootInterface()?.config?` into a single accessor,
`authentikConfig`, that can be mixed into any AKElement object that requires access to it.
Since access to `rootInterface()?.config?` is _universally_ used for a single (and repetitive)
boolean check, a separate accessor has been provided that converts all calls of the form:
``` javascript
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate)
```
into:
``` javascript
this.can(CapabilitiesEnum.CanImpersonate)
```
It does this via a Mixin, `WithCapabilitiesConfig`, which understands that these calls only make
sense in the context of a running, fully configured authentik instance, and that their purpose is to
inform authentik components of a user’s capabilities. The latter is why I don’t feel uncomfortable
turning a function call into a method; we should make it explicit that this is a relationship
between components.
The mixin has a single single field, `[WCC.capabilitiesConfig]`, where its association with the
upper-level configuration is made. If that syntax looks peculiar to you, good! I’ve used an explict
unique symbol as the field name; it is inaccessable an innumerable in the object list. The debugger
shows it only as:
Symbol(): {
cacheTimeout: 300
cacheTimeoutFlows: 300
cacheTimeoutPolicies: 300
cacheTimeoutReputation: 300
capabilities: (5) ['can_save_media', 'can_geo_ip', 'can_impersonate', 'can_debug', 'is_enterprise']
}
Since you can’t reference it by identity, you can’t write to it. Until every browser supports actual
private fields, this is the best we can do; it does guarantee that field name collisions are
impossible, which is a win.
The mixin takes a second optional boolean; setting this to true will cause any web component using
the mixin to automatically schedule a re-render if the capabilities list changes.
The mixin is also generic; despite the "...into a Lit-Context" in the title, the internals of the
Mixin can be replaced with anything so long as the signature of `.can()` is preserved.
Because this work builds off the work I did to give the Sidebar access to the configuration without
ad-hoc retrieval or prop-drilling, it wasn’t necessary to create a new context for it. That will be
necessary for the following:
TODO:
``` javascript
rootInterface()?.uiConfig;
rootInterface()?.tenant;
me();
```
* web: Added a README with a description of the applications' "mental model," essentially an architectural description.
* web: prettier had opinions about the README
* web: Jens requested that subscription be by default, and it's the right call.
* web: adjust RAC to point to the (now independent) Interface.
- Also, removed redundant check.
This commit is contained in:
@ -7,7 +7,7 @@ import {
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
|
||||
@ -1,23 +1,25 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_SIDEBAR_TOGGLE, VERSION } from "@goauthentik/common/constants";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
||||
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
|
||||
import { spread } from "@open-wc/lit-helpers";
|
||||
|
||||
import { consume } from "@lit-labs/context";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { map } from "lit/directives/map.js";
|
||||
|
||||
import { AdminApi, CapabilitiesEnum, CoreApi, UiThemeEnum, Version } from "@goauthentik/api";
|
||||
import type { Config, SessionUser, UserSelf } from "@goauthentik/api";
|
||||
import { AdminApi, CoreApi, UiThemeEnum, Version } from "@goauthentik/api";
|
||||
import type { SessionUser, UserSelf } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-admin-sidebar")
|
||||
export class AkAdminSidebar extends AKElement {
|
||||
export class AkAdminSidebar extends WithCapabilitiesConfig(AKElement) {
|
||||
@property({ type: Boolean, reflect: true })
|
||||
open = true;
|
||||
|
||||
@ -27,9 +29,6 @@ export class AkAdminSidebar extends AKElement {
|
||||
@state()
|
||||
impersonation: UserSelf["username"] | null = null;
|
||||
|
||||
@consume({ context: authentikConfigContext })
|
||||
public config!: Config;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve().then((version) => {
|
||||
@ -200,7 +199,7 @@ export class AkAdminSidebar extends AKElement {
|
||||
}
|
||||
|
||||
renderEnterpriseMessage() {
|
||||
return this.config?.capabilities.includes(CapabilitiesEnum.IsEnterprise)
|
||||
return this.can(CapabilitiesEnum.IsEnterprise)
|
||||
? html`
|
||||
<ak-sidebar-item>
|
||||
<span slot="label">${msg("Enterprise")}</span>
|
||||
|
||||
@ -74,10 +74,7 @@ export class AdminOverviewPage extends AKElement {
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
let name = this.user?.user.username;
|
||||
if (this.user?.user.name) {
|
||||
name = this.user.user.name;
|
||||
}
|
||||
const name = this.user?.user.name ?? this.user?.user.username;
|
||||
return html`<ak-page-header icon="" header="" description=${msg("General system status")}>
|
||||
<span slot="header"> ${msg(str`Welcome, ${name}.`)} </span>
|
||||
</ak-page-header>
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import "@goauthentik/admin/applications/ProviderSelectModal";
|
||||
import { iconHelperText } from "@goauthentik/admin/helperText";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-file-input";
|
||||
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 { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
@ -22,13 +25,7 @@ import { TemplateResult, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
Application,
|
||||
CapabilitiesEnum,
|
||||
CoreApi,
|
||||
PolicyEngineMode,
|
||||
Provider,
|
||||
} from "@goauthentik/api";
|
||||
import { Application, CoreApi, PolicyEngineMode, Provider } from "@goauthentik/api";
|
||||
|
||||
import "./components/ak-backchannel-input";
|
||||
import "./components/ak-provider-search-input";
|
||||
@ -48,7 +45,7 @@ export const policyOptions = [
|
||||
];
|
||||
|
||||
@customElement("ak-application-form")
|
||||
export class ApplicationForm extends ModelForm<Application, string> {
|
||||
export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Application, string>) {
|
||||
constructor() {
|
||||
super();
|
||||
this.handleConfirmBackchannelProviders = this.handleConfirmBackchannelProviders.bind(this);
|
||||
@ -93,8 +90,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
applicationRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["metaIcon"];
|
||||
if (icon || this.clearIcon) {
|
||||
await new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({
|
||||
@ -140,21 +136,21 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-text-input
|
||||
name="name"
|
||||
.value=${this.instance?.name}
|
||||
value=${ifDefined(this.instance?.name)}
|
||||
label=${msg("Name")}
|
||||
required
|
||||
help=${msg("Application's display Name.")}
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="slug"
|
||||
.value=${this.instance?.slug}
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
help=${msg("Internal application name used in URLs.")}
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="group"
|
||||
.value=${this.instance?.group}
|
||||
value=${ifDefined(this.instance?.group)}
|
||||
label=${msg("Group")}
|
||||
help=${msg(
|
||||
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
|
||||
@ -163,7 +159,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
<ak-provider-search-input
|
||||
name="provider"
|
||||
label=${msg("Provider")}
|
||||
.value=${this.instance?.provider}
|
||||
value=${ifDefined(this.instance?.provider ?? undefined)}
|
||||
help=${msg("Select a provider that this application should use.")}
|
||||
blankable
|
||||
></ak-provider-search-input>
|
||||
@ -209,11 +205,11 @@ export class ApplicationForm extends ModelForm<Application, string> {
|
||||
)}
|
||||
>
|
||||
</ak-switch-input>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-file-input
|
||||
label="${msg("Icon")}"
|
||||
name="metaIcon"
|
||||
.value=${this.instance?.metaIcon}
|
||||
value=${ifDefined(this.instance?.metaIcon ?? undefined)}
|
||||
current=${msg("Currently set to:")}
|
||||
></ak-file-input>
|
||||
${this.instance?.metaIcon
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils";
|
||||
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
@ -14,7 +17,6 @@ import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
DeniedActionEnum,
|
||||
Flow,
|
||||
FlowDesignationEnum,
|
||||
@ -24,7 +26,7 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-flow-form")
|
||||
export class FlowForm extends ModelForm<Flow, string> {
|
||||
export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
async loadInstance(pk: string): Promise<Flow> {
|
||||
const flow = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({
|
||||
slug: pk,
|
||||
@ -54,8 +56,8 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||
flowRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
|
||||
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["background"];
|
||||
if (icon || this.clearBackground) {
|
||||
await new FlowsApi(DEFAULT_CONFIG).flowsInstancesSetBackgroundCreate({
|
||||
@ -340,7 +342,7 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal
|
||||
label=${msg("Background")}
|
||||
name="background"
|
||||
|
||||
@ -118,7 +118,7 @@ export class GroupViewPage extends AKElement {
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-status-label
|
||||
type="warning"
|
||||
?good=${this.group.isSuperuser}
|
||||
?good${this.group.isSuperuser}
|
||||
></ak-status-label>
|
||||
</div>
|
||||
</dd>
|
||||
|
||||
@ -10,6 +10,10 @@ import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/buttons/Dropdown";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
@ -33,7 +37,6 @@ import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
CoreApi,
|
||||
CoreUsersListTypeEnum,
|
||||
Group,
|
||||
@ -107,7 +110,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
}
|
||||
|
||||
@customElement("ak-user-related-list")
|
||||
export class RelatedUserList extends Table<User> {
|
||||
export class RelatedUserList extends WithCapabilitiesConfig(Table<User>) {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
|
||||
@ -188,8 +191,7 @@ export class RelatedUserList extends Table<User> {
|
||||
|
||||
row(item: User): TemplateResult[] {
|
||||
const canImpersonate =
|
||||
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
|
||||
item.pk !== this.me?.user.pk;
|
||||
this.can(CapabilitiesEnum.CanImpersonate) && item.pk !== this.me?.user.pk;
|
||||
return [
|
||||
html`<a href="#/identity/users/${item.pk}">
|
||||
<div>${item.username}</div>
|
||||
|
||||
@ -4,9 +4,12 @@ import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
@ -17,7 +20,6 @@ import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
OAuthSource,
|
||||
OAuthSourceRequest,
|
||||
@ -28,7 +30,7 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-source-oauth-form")
|
||||
export class OAuthSourceForm extends BaseSourceForm<OAuthSource> {
|
||||
export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuthSource>) {
|
||||
async loadInstance(pk: string): Promise<OAuthSource> {
|
||||
const source = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthRetrieve({
|
||||
slug: pk,
|
||||
@ -318,7 +320,7 @@ export class OAuthSourceForm extends BaseSourceForm<OAuthSource> {
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${placeholderHelperText}</p>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
|
||||
@ -2,10 +2,13 @@ import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
|
||||
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
|
||||
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
@ -16,7 +19,6 @@ import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PlexSource,
|
||||
SourcesApi,
|
||||
@ -24,7 +26,7 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-source-plex-form")
|
||||
export class PlexSourceForm extends BaseSourceForm<PlexSource> {
|
||||
export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm<PlexSource>) {
|
||||
async loadInstance(pk: string): Promise<PlexSource> {
|
||||
const source = await new SourcesApi(DEFAULT_CONFIG).sourcesPlexRetrieve({
|
||||
slug: pk,
|
||||
@ -63,8 +65,7 @@ export class PlexSourceForm extends BaseSourceForm<PlexSource> {
|
||||
plexSourceRequest: data,
|
||||
});
|
||||
}
|
||||
const c = await config();
|
||||
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["icon"];
|
||||
if (icon || this.clearIcon) {
|
||||
await new SourcesApi(DEFAULT_CONFIG).sourcesAllSetIconCreate({
|
||||
@ -255,7 +256,7 @@ export class PlexSourceForm extends BaseSourceForm<PlexSource> {
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${placeholderHelperText}</p>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
|
||||
@ -5,7 +5,10 @@ import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
@ -18,7 +21,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
BindingTypeEnum,
|
||||
CapabilitiesEnum,
|
||||
DigestAlgorithmEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
NameIdPolicyEnum,
|
||||
@ -29,7 +31,7 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-source-saml-form")
|
||||
export class SAMLSourceForm extends BaseSourceForm<SAMLSource> {
|
||||
export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSource>) {
|
||||
@state()
|
||||
clearIcon = false;
|
||||
|
||||
@ -149,7 +151,7 @@ export class SAMLSourceForm extends BaseSourceForm<SAMLSource> {
|
||||
</option>
|
||||
</select>
|
||||
</ak-form-element-horizontal>
|
||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
|
||||
${this.can(CapabilitiesEnum.CanSaveMedia)
|
||||
? html`<ak-form-element-horizontal label=${msg("Icon")} name="icon">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
${this.instance?.icon
|
||||
|
||||
@ -12,6 +12,10 @@ import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import { PFSize } from "@goauthentik/elements/Spinner";
|
||||
import "@goauthentik/elements/TreeView";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
@ -33,14 +37,7 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
CoreApi,
|
||||
ResponseError,
|
||||
SessionUser,
|
||||
User,
|
||||
UserPath,
|
||||
} from "@goauthentik/api";
|
||||
import { CoreApi, ResponseError, SessionUser, User, UserPath } from "@goauthentik/api";
|
||||
|
||||
export const requestRecoveryLink = (user: User) =>
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
@ -93,7 +90,7 @@ const recoveryButtonStyles = css`
|
||||
`;
|
||||
|
||||
@customElement("ak-user-list")
|
||||
export class UserListPage extends TablePage<User> {
|
||||
export class UserListPage extends WithCapabilitiesConfig(TablePage<User>) {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
|
||||
@ -244,8 +241,7 @@ export class UserListPage extends TablePage<User> {
|
||||
|
||||
row(item: User): TemplateResult[] {
|
||||
const canImpersonate =
|
||||
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
|
||||
item.pk !== this.me?.user.pk;
|
||||
this.can(CapabilitiesEnum.CanImpersonate) && item.pk !== this.me?.user.pk;
|
||||
return [
|
||||
html`<a href="#/identity/users/${item.pk}">
|
||||
<div>${item.username}</div>
|
||||
|
||||
@ -22,8 +22,9 @@ import {
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import "@goauthentik/components/events/UserEvents";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { WithCapabilitiesConfig } from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/PageHeader";
|
||||
import { PFSize } from "@goauthentik/elements/Spinner";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
@ -60,7 +61,7 @@ import {
|
||||
import "./UserDevicesTable";
|
||||
|
||||
@customElement("ak-user-view")
|
||||
export class UserViewPage extends AKElement {
|
||||
export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
|
||||
@property({ type: Number })
|
||||
set userId(id: number) {
|
||||
me().then((me) => {
|
||||
@ -163,8 +164,7 @@ export class UserViewPage extends AKElement {
|
||||
|
||||
renderActionButtons(user: User) {
|
||||
const canImpersonate =
|
||||
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
|
||||
user.pk !== this.me?.user.pk;
|
||||
this.can(CapabilitiesEnum.CanImpersonate) && user.pk !== this.me?.user.pk;
|
||||
|
||||
return html`<div class="ak-button-collection">
|
||||
<ak-forms-modal>
|
||||
|
||||
Reference in New Issue
Block a user