From a2dce3fb635266c0d2e85673664a41308b542940 Mon Sep 17 00:00:00 2001
From: Ken Sternberg
<133134217+kensternberg-authentik@users.noreply.github.com>
Date: Mon, 8 Jan 2024 13:03:00 -0800
Subject: [PATCH] web: Replace calls to `rootInterface()?.tenant?` with a
contextual `this.tenant` object (#7778)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 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();
```
* This commit abstracts access to the object `rootInterface()?.tenant?` into a single accessor,
`tenant`, that can be mixed into any AKElement object that requires access to it.
Like `WithCapabilitiesConfig` and `WithAuthentikConfig`, this one is named `WithTenantConfig`.
TODO:
``` javascript
rootInterface()?.uiConfig;
me();
```
* web: Added a README with a description of the applications' "mental model," essentially an architectural description.
* web: prettier did a thing
* web: prettier had opinions about the README
* web: Jens requested that subscription be by default, and it's the right call.
* web: Jens requested that the default subscription state for contexts be , and it's the right call.
* web: prettier having opinions after merging with dependent branch
* web: prettier still having opinions.
---
...plication-wizard-authentication-by-ldap.ts | 6 ++---
...ication-wizard-authentication-by-radius.ts | 6 ++---
web/src/admin/groups/RelatedUserList.ts | 6 ++---
.../admin/providers/ldap/LDAPProviderForm.ts | 6 ++---
.../providers/radius/RadiusProviderForm.ts | 6 ++---
web/src/admin/users/UserListPage.ts | 5 ++--
web/src/elements/AuthentikContexts.ts | 6 ++++-
web/src/elements/Interface/Interface.ts | 26 ++++++++++++++++---
.../Interface/authentikConfigProvider.ts | 2 +-
web/src/elements/Interface/tenantProvider.ts | 20 ++++++++++++++
web/src/elements/PageHeader.ts | 8 +++---
web/src/elements/sidebar/SidebarBrand.ts | 11 +++-----
.../details/UserSettingsFlowExecutor.ts | 15 +++++------
13 files changed, 81 insertions(+), 42 deletions(-)
create mode 100644 web/src/elements/Interface/tenantProvider.ts
diff --git a/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts
index a99384171..6e1554196 100644
--- a/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts
+++ b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts
@@ -7,7 +7,7 @@ import "@goauthentik/components/ak-number-input";
import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
-import { rootInterface } from "@goauthentik/elements/Base";
+import { WithTenantConfig } from "@goauthentik/elements/Interface/tenantProvider";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
@@ -32,7 +32,7 @@ import {
} from "./LDAPOptionsAndHelp";
@customElement("ak-application-wizard-authentication-by-ldap")
-export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
+export class ApplicationWizardApplicationDetails extends WithTenantConfig(BaseProviderPanel) {
render() {
const provider = this.wizard.provider as LDAPProvider | undefined;
const errors = this.wizard.errors.provider;
@@ -57,7 +57,7 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
diff --git a/web/src/admin/applications/wizard/methods/radius/ak-application-wizard-authentication-by-radius.ts b/web/src/admin/applications/wizard/methods/radius/ak-application-wizard-authentication-by-radius.ts
index cadbd94ad..44f452037 100644
--- a/web/src/admin/applications/wizard/methods/radius/ak-application-wizard-authentication-by-radius.ts
+++ b/web/src/admin/applications/wizard/methods/radius/ak-application-wizard-authentication-by-radius.ts
@@ -3,7 +3,7 @@ import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-text-input";
-import { rootInterface } from "@goauthentik/elements/Base";
+import { WithTenantConfig } from "@goauthentik/elements/Interface/tenantProvider";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
@@ -17,7 +17,7 @@ import { FlowsInstancesListDesignationEnum, RadiusProvider } from "@goauthentik/
import BaseProviderPanel from "../BaseProviderPanel";
@customElement("ak-application-wizard-authentication-by-radius")
-export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
+export class ApplicationWizardAuthenticationByRadius extends WithTenantConfig(BaseProviderPanel) {
render() {
const provider = this.wizard.provider as RadiusProvider | undefined;
const errors = this.wizard.errors.provider;
@@ -42,7 +42,7 @@ export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
diff --git a/web/src/admin/groups/RelatedUserList.ts b/web/src/admin/groups/RelatedUserList.ts
index 27450fb82..5e2c6b952 100644
--- a/web/src/admin/groups/RelatedUserList.ts
+++ b/web/src/admin/groups/RelatedUserList.ts
@@ -9,11 +9,11 @@ import { MessageLevel } from "@goauthentik/common/messages";
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 { WithTenantConfig } from "@goauthentik/elements/Interface/tenantProvider";
import "@goauthentik/elements/buttons/ActionButton";
import "@goauthentik/elements/buttons/Dropdown";
import "@goauthentik/elements/forms/DeleteBulkForm";
@@ -110,7 +110,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
}
@customElement("ak-user-related-list")
-export class RelatedUserList extends WithCapabilitiesConfig(Table ${msg("Flow used for users to authenticate.")} ${msg("Flow used for users to authenticate.")}
diff --git a/web/src/user/user-settings/details/UserSettingsFlowExecutor.ts b/web/src/user/user-settings/details/UserSettingsFlowExecutor.ts
index e7aa36343..f4252f58b 100644
--- a/web/src/user/user-settings/details/UserSettingsFlowExecutor.ts
+++ b/web/src/user/user-settings/details/UserSettingsFlowExecutor.ts
@@ -2,14 +2,15 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { MessageLevel } from "@goauthentik/common/messages";
import { refreshMe } from "@goauthentik/common/users";
-import { AKElement, rootInterface } from "@goauthentik/elements/Base";
+import { AKElement } from "@goauthentik/elements/Base";
+import { WithTenantConfig } from "@goauthentik/elements/Interface/tenantProvider";
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
import { StageHost } from "@goauthentik/flow/stages/base";
import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
-import { customElement, property, state } from "lit/decorators.js";
+import { customElement, property } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
@@ -21,7 +22,6 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import {
ChallengeChoices,
ChallengeTypes,
- CurrentTenant,
FlowChallengeResponseRequest,
FlowErrorChallenge,
FlowsApi,
@@ -31,13 +31,13 @@ import {
} from "@goauthentik/api";
@customElement("ak-user-settings-flow-executor")
-export class UserSettingsFlowExecutor extends AKElement implements StageHost {
+export class UserSettingsFlowExecutor
+ extends WithTenantConfig(AKElement, true)
+ implements StageHost
+{
@property()
flowSlug?: string;
- @state()
- tenant?: CurrentTenant;
-
private _challenge?: ChallengeTypes;
@property({ attribute: false })
@@ -87,7 +87,6 @@ export class UserSettingsFlowExecutor extends AKElement implements StageHost {
}
firstUpdated(): void {
- this.tenant = rootInterface()?.tenant;
this.flowSlug = this.tenant?.flowUserSettings;
if (!this.flowSlug) {
return;