From 869f18483f3431a3377c52d5ed93f3b06023b08a Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 27 Mar 2021 22:02:01 +0100 Subject: [PATCH 1/9] web: fix flow's policy and stage count missing Signed-off-by: Jens Langhammer --- web/src/pages/flows/FlowListPage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/pages/flows/FlowListPage.ts b/web/src/pages/flows/FlowListPage.ts index 84c7d9d76..1dc88d311 100644 --- a/web/src/pages/flows/FlowListPage.ts +++ b/web/src/pages/flows/FlowListPage.ts @@ -57,8 +57,8 @@ export class FlowListPage extends TablePage { `, html`${item.name}`, html`${item.designation}`, - html`${item.stages?.size}`, - html`${item.policies?.size}`, + html`${Array.from(item.stages || []).length}`, + html`${Array.from(item.policies || []).length}`, html` From c8608db4ee31bb99b7c79471a94bd9fbb3518ec0 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 27 Mar 2021 22:06:42 +0100 Subject: [PATCH 2/9] web: cleanup entrypoints Signed-off-by: Jens Langhammer --- web/rollup.config.js | 4 ++-- web/src/flow.ts | 4 ---- web/src/interfaces/AdminInterface.ts | 6 ++++++ web/src/interfaces/FlowInterface.ts | 4 ++++ web/src/main.ts | 14 -------------- web/src/pages/users/UserSettingsPage.ts | 16 ++-------------- 6 files changed, 14 insertions(+), 34 deletions(-) delete mode 100644 web/src/flow.ts create mode 100644 web/src/interfaces/FlowInterface.ts delete mode 100644 web/src/main.ts diff --git a/web/rollup.config.js b/web/rollup.config.js index e86f10842..84045077d 100644 --- a/web/rollup.config.js +++ b/web/rollup.config.js @@ -65,7 +65,7 @@ export default [ }, // Main Application { - input: "./src/main.ts", + input: "./src/interfaces/AdminInterface.ts", output: [ { format: "es", @@ -92,7 +92,7 @@ export default [ }, // Flow executor { - input: "./src/flow.ts", + input: "./src/interfaces/FlowInterface.ts", output: [ { format: "es", diff --git a/web/src/flow.ts b/web/src/flow.ts deleted file mode 100644 index 9fe22476c..000000000 --- a/web/src/flow.ts +++ /dev/null @@ -1,4 +0,0 @@ -import "construct-style-sheets-polyfill"; - -import "./elements/messages/MessageContainer"; -import "./flows/FlowExecutor"; diff --git a/web/src/interfaces/AdminInterface.ts b/web/src/interfaces/AdminInterface.ts index a31b5b48a..0418c459e 100644 --- a/web/src/interfaces/AdminInterface.ts +++ b/web/src/interfaces/AdminInterface.ts @@ -1,3 +1,9 @@ +import "construct-style-sheets-polyfill"; + +// Elements that are used by SiteShell pages +// And can't dynamically be imported +import "../elements/CodeMirror"; +import "../elements/messages/MessageContainer"; import { customElement } from "lit-element"; import { me } from "../api/Users"; import { SidebarItem } from "../elements/sidebar/Sidebar"; diff --git a/web/src/interfaces/FlowInterface.ts b/web/src/interfaces/FlowInterface.ts new file mode 100644 index 000000000..9ba725a8c --- /dev/null +++ b/web/src/interfaces/FlowInterface.ts @@ -0,0 +1,4 @@ +import "construct-style-sheets-polyfill"; + +import "../elements/messages/MessageContainer"; +import "../flows/FlowExecutor"; diff --git a/web/src/main.ts b/web/src/main.ts deleted file mode 100644 index f57e82208..000000000 --- a/web/src/main.ts +++ /dev/null @@ -1,14 +0,0 @@ -import "construct-style-sheets-polyfill"; - -// Elements that are used by SiteShell pages -// And can't dynamically be imported -import "./elements/buttons/ActionButton"; -import "./elements/buttons/Dropdown"; -import "./elements/buttons/ModalButton"; -import "./elements/buttons/SpinnerButton"; -import "./elements/CodeMirror"; - -import "./pages/tokens/UserTokenList"; -import "./pages/generic/SiteShell"; -import "./interfaces/AdminInterface"; -import "./elements/messages/MessageContainer"; diff --git a/web/src/pages/users/UserSettingsPage.ts b/web/src/pages/users/UserSettingsPage.ts index eefdae207..d6590324e 100644 --- a/web/src/pages/users/UserSettingsPage.ts +++ b/web/src/pages/users/UserSettingsPage.ts @@ -48,13 +48,7 @@ export class UserSettingsPage extends LitElement { return html` `; default: - return html`
-
- -
-
-
-
`; + return html`

unsupported component ${stage.component}

`; } } @@ -64,13 +58,7 @@ export class UserSettingsPage extends LitElement { return html` `; default: - return html`
-
- -
-
-
-
`; + return html`

unsupported component ${source.component}

`; } } From 103e0f3b06f3eae8005ce0826e4caebd2b69cf9e Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 27 Mar 2021 22:15:20 +0100 Subject: [PATCH 3/9] web: add default title Signed-off-by: Jens Langhammer --- authentik/core/templates/if/admin.html | 2 +- authentik/core/templates/if/flow.html | 2 +- web/src/interfaces/admin/index.html | 3 ++- web/src/interfaces/flow/index.html | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/authentik/core/templates/if/admin.html b/authentik/core/templates/if/admin.html index 243772a85..43df9a509 100644 --- a/authentik/core/templates/if/admin.html +++ b/authentik/core/templates/if/admin.html @@ -3,7 +3,7 @@ {% load static %} {% block head %} - + {% endblock %} {% block body %} diff --git a/authentik/core/templates/if/flow.html b/authentik/core/templates/if/flow.html index 0197da38d..3756f4f89 100644 --- a/authentik/core/templates/if/flow.html +++ b/authentik/core/templates/if/flow.html @@ -3,7 +3,7 @@ {% load static %} {% block head %} - + {% endblock %} {% block body %} diff --git a/web/src/interfaces/admin/index.html b/web/src/interfaces/admin/index.html index 793861dac..dcaad682b 100644 --- a/web/src/interfaces/admin/index.html +++ b/web/src/interfaces/admin/index.html @@ -8,7 +8,8 @@ - + + authentik diff --git a/web/src/interfaces/flow/index.html b/web/src/interfaces/flow/index.html index 9d3071349..ceec0869c 100644 --- a/web/src/interfaces/flow/index.html +++ b/web/src/interfaces/flow/index.html @@ -8,7 +8,8 @@ - + + authentik From 9bab708e6e48bb4aa1156b7a8c3ef71a17253241 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 27 Mar 2021 22:27:32 +0100 Subject: [PATCH 4/9] web/flows/authenticator_validate: auto-select TOTP device when password manager prefills it Signed-off-by: Jens Langhammer --- .../AuthenticatorValidateStage.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts b/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts index 21c49bbf2..e048cf178 100644 --- a/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts +++ b/web/src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts @@ -11,6 +11,7 @@ import AKGlobal from "../../../authentik.css"; import { BaseStage, StageHost } from "../base"; import "./AuthenticatorValidateStageWebAuthn"; import "./AuthenticatorValidateStageCode"; +import { PasswordManagerPrefill } from "../identification/IdentificationStage"; export enum DeviceClasses { STATIC = "static", @@ -83,6 +84,17 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost { ${gettext("Use a security key to prove your identity.")} `; case DeviceClasses.TOTP: + // TOTP is a bit special, assuming that TOTP is allowed from the backend, + // and we have a pre-filled value from the password manager, + // directly set the the TOTP device Challenge as active. + if (PasswordManagerPrefill.totp) { + console.debug("authentik/stages/authenticator_validate: found prefill totp code, selecting totp challenge"); + this.selectedDeviceChallenge = deviceChallenge; + // Delay the update as a re-render isn't triggered from here + setTimeout(() => { + this.requestUpdate(); + }, 100); + } return html`

${gettext("Traditional authenticator")}

@@ -141,9 +153,9 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost { render(): TemplateResult { if (!this.challenge) { return html` -`; + ?loading="${true}" + header=${gettext("Loading")}> + `; } // User only has a single device class, so we don't show a picker if (this.challenge?.device_challenges.length === 1) { From 1bc48d2bea55245a3de9d3d4765c9c2eb2ce51e8 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 27 Mar 2021 22:32:29 +0100 Subject: [PATCH 5/9] stages/dummy: fix missing component Signed-off-by: Jens Langhammer --- authentik/stages/dummy/stage.py | 2 +- web/src/flows/FlowExecutor.ts | 5 +- .../flows/access_denied/FlowAccessDenied.ts | 22 -------- web/src/flows/stages/dummy/DummyStage.ts | 52 +++++++++++++++++++ 4 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 web/src/flows/stages/dummy/DummyStage.ts diff --git a/authentik/stages/dummy/stage.py b/authentik/stages/dummy/stage.py index 10f5ad3ca..b4c2f844c 100644 --- a/authentik/stages/dummy/stage.py +++ b/authentik/stages/dummy/stage.py @@ -25,7 +25,7 @@ class DummyStageView(ChallengeStageView): return DummyChallenge( data={ "type": ChallengeTypes.native.value, - "component": "", + "component": "ak-stage-dummy", "title": self.executor.current_stage.name, } ) diff --git a/web/src/flows/FlowExecutor.ts b/web/src/flows/FlowExecutor.ts index f8e63a72f..10297e2b4 100644 --- a/web/src/flows/FlowExecutor.ts +++ b/web/src/flows/FlowExecutor.ts @@ -9,6 +9,7 @@ import PFList from "@patternfly/patternfly/components/List/list.css"; import AKGlobal from "../authentik.css"; import { unsafeHTML } from "lit-html/directives/unsafe-html"; +import "./access_denied/FlowAccessDenied"; import "./stages/authenticator_static/AuthenticatorStaticStage"; import "./stages/authenticator_totp/AuthenticatorTOTPStage"; import "./stages/authenticator_validate/AuthenticatorValidateStage"; @@ -16,11 +17,11 @@ import "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; import "./stages/autosubmit/AutosubmitStage"; import "./stages/captcha/CaptchaStage"; import "./stages/consent/ConsentStage"; +import "./stages/dummy/DummyStage"; import "./stages/email/EmailStage"; import "./stages/identification/IdentificationStage"; import "./stages/password/PasswordStage"; import "./stages/prompt/PromptStage"; -import "./access_denied/FlowAccessDenied"; import { ShellChallenge, RedirectChallenge } from "../api/Flows"; import { IdentificationChallenge } from "./stages/identification/IdentificationStage"; import { PasswordChallenge } from "./stages/password/PasswordStage"; @@ -193,6 +194,8 @@ export class FlowExecutor extends LitElement implements StageHost { return html``; case "ak-stage-consent": return html``; + case "ak-stage-dummy": + return html``; case "ak-stage-email": return html``; case "ak-stage-autosubmit": diff --git a/web/src/flows/access_denied/FlowAccessDenied.ts b/web/src/flows/access_denied/FlowAccessDenied.ts index e6fe27c79..bdbd3df16 100644 --- a/web/src/flows/access_denied/FlowAccessDenied.ts +++ b/web/src/flows/access_denied/FlowAccessDenied.ts @@ -14,7 +14,6 @@ import "../../elements/EmptyState"; export interface AccessDeniedChallenge extends Challenge { error_message?: string; - policy_result?: Record; } @customElement("ak-stage-access-denied") @@ -49,27 +48,6 @@ export class FlowAccessDenied extends BaseStage { ${this.challenge?.error_message && html`

${this.challenge.error_message}

`} - ${this.challenge.policy_result && - html`
- - ${gettext("Explanation:")} - -
    - {% for source_result in policy_result.source_results %} -
  • - {% blocktrans with name=source_result.source_policy.name result=source_result.passing %} - Policy '{{ name }}' returned result '{{ result }}' - {% endblocktrans %} - {% if source_result.messages %} -
      - {% for message in source_result.messages %} -
    • {{ message }}
    • - {% endfor %} -
    - {% endif %} -
  • - {% endfor %} -
`}
diff --git a/web/src/flows/stages/dummy/DummyStage.ts b/web/src/flows/stages/dummy/DummyStage.ts new file mode 100644 index 000000000..d54d3389c --- /dev/null +++ b/web/src/flows/stages/dummy/DummyStage.ts @@ -0,0 +1,52 @@ +import { gettext } from "django"; +import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; +import { Challenge } from "../../../api/Flows"; +import PFLogin from "@patternfly/patternfly/components/Login/login.css"; +import PFForm from "@patternfly/patternfly/components/Form/form.css"; +import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; +import PFTitle from "@patternfly/patternfly/components/Title/title.css"; +import PFButton from "@patternfly/patternfly/components/Button/button.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; +import AKGlobal from "../../../authentik.css"; +import { BaseStage } from "../base"; +import "../../../elements/EmptyState"; +import "../../FormStatic"; + +@customElement("ak-stage-dummy") +export class DummyStage extends BaseStage { + + @property({ attribute: false }) + challenge?: Challenge; + + static get styles(): CSSResult[] { + return [PFBase, PFLogin, PFForm, PFFormControl, PFTitle, PFButton, AKGlobal]; + } + + render(): TemplateResult { + if (!this.challenge) { + return html` + `; + } + return html` + +
+ +
`; + } + +} From 261583cb922543f7270677489437d69819261fe6 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 27 Mar 2021 22:58:41 +0100 Subject: [PATCH 6/9] flows: fix tests for dummy stage Signed-off-by: Jens Langhammer --- authentik/flows/tests/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentik/flows/tests/test_views.py b/authentik/flows/tests/test_views.py index 7c4f294ff..6664ea28f 100644 --- a/authentik/flows/tests/test_views.py +++ b/authentik/flows/tests/test_views.py @@ -416,7 +416,7 @@ class TestFlowExecutor(TestCase): { "background": flow.background.url, "type": ChallengeTypes.native.value, - "component": "", + "component": "ak-stage-dummy", "title": binding.stage.name, }, ) From e621eb74550cbcb457f7e2a8a4ad0cafcbce74cf Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 27 Mar 2021 23:11:44 +0100 Subject: [PATCH 7/9] web: cleanup message API, use enum for level Signed-off-by: Jens Langhammer --- authentik/root/messages/storage.py | 2 +- web/src/elements/buttons/ActionButton.ts | 5 +++-- web/src/elements/buttons/ModalButton.ts | 5 +++-- web/src/elements/forms/ConfirmationForm.ts | 5 +++-- web/src/elements/forms/DeleteForm.ts | 5 +++-- web/src/elements/messages/Message.ts | 13 ++++++++++--- web/src/elements/messages/MessageContainer.ts | 4 ++-- .../authenticator_totp/AuthenticatorTOTPStage.ts | 3 ++- web/src/pages/generic/SiteShell.ts | 5 +++-- web/src/pages/users/UserActiveForm.ts | 5 +++-- 10 files changed, 33 insertions(+), 19 deletions(-) diff --git a/authentik/root/messages/storage.py b/authentik/root/messages/storage.py index 0999c276c..4e23ca729 100644 --- a/authentik/root/messages/storage.py +++ b/authentik/root/messages/storage.py @@ -25,7 +25,7 @@ class ChannelsStorage(FallbackStorage): uid, { "type": "event.update", - "level_tag": message.level_tag, + "level": message.level_tag, "tags": message.tags, "message": message.message, }, diff --git a/web/src/elements/buttons/ActionButton.ts b/web/src/elements/buttons/ActionButton.ts index 868c99b36..6e3826f01 100644 --- a/web/src/elements/buttons/ActionButton.ts +++ b/web/src/elements/buttons/ActionButton.ts @@ -2,6 +2,7 @@ import { customElement, property } from "lit-element"; import { ERROR_CLASS, SUCCESS_CLASS } from "../../constants"; import { SpinnerButton } from "./SpinnerButton"; import { showMessage } from "../messages/MessageContainer"; +import { MessageLevel } from "../messages/Message"; @customElement("ak-action-button") export class ActionButton extends SpinnerButton { @@ -26,13 +27,13 @@ export class ActionButton extends SpinnerButton { .catch((e: Error | Response) => { if (e instanceof Error) { showMessage({ - level_tag: "error", + level: MessageLevel.error, message: e.toString() }); } else { e.text().then(t => { showMessage({ - level_tag: "error", + level: MessageLevel.error, message: t }); }); diff --git a/web/src/elements/buttons/ModalButton.ts b/web/src/elements/buttons/ModalButton.ts index 111fc0d04..34061b451 100644 --- a/web/src/elements/buttons/ModalButton.ts +++ b/web/src/elements/buttons/ModalButton.ts @@ -19,6 +19,7 @@ import { convertToSlug } from "../../utils"; import { SpinnerButton } from "./SpinnerButton"; import { PRIMARY_CLASS, EVENT_REFRESH } from "../../constants"; import { showMessage } from "../messages/MessageContainer"; +import { MessageLevel } from "../messages/Message"; @customElement("ak-modal-button") export class ModalButton extends LitElement { @@ -122,7 +123,7 @@ export class ModalButton extends LitElement { }) .catch((e) => { showMessage({ - level_tag: "error", + level: MessageLevel.error, message: "Unexpected error" }); console.error(e); @@ -150,7 +151,7 @@ export class ModalButton extends LitElement { }) .catch((e) => { showMessage({ - level_tag: "error", + level: MessageLevel.error, message: "Unexpected error" }); console.error(e); diff --git a/web/src/elements/forms/ConfirmationForm.ts b/web/src/elements/forms/ConfirmationForm.ts index edc2dbf55..0bfdfd3b2 100644 --- a/web/src/elements/forms/ConfirmationForm.ts +++ b/web/src/elements/forms/ConfirmationForm.ts @@ -2,6 +2,7 @@ import { gettext } from "django"; import { customElement, html, property, TemplateResult } from "lit-element"; import { EVENT_REFRESH } from "../../constants"; import { ModalButton } from "../buttons/ModalButton"; +import { MessageLevel } from "../messages/Message"; import { showMessage } from "../messages/MessageContainer"; @customElement("ak-forms-confirm") @@ -36,14 +37,14 @@ export class ConfirmationForm extends ModalButton { onSuccess(): void { showMessage({ message: gettext(this.successMessage), - level_tag: "success", + level: MessageLevel.success, }); } onError(e: Error): void { showMessage({ message: gettext(`${this.errorMessage}: ${e.toString()}`), - level_tag: "error", + level: MessageLevel.error, }); } diff --git a/web/src/elements/forms/DeleteForm.ts b/web/src/elements/forms/DeleteForm.ts index 7fbce8d8e..45c1e8d09 100644 --- a/web/src/elements/forms/DeleteForm.ts +++ b/web/src/elements/forms/DeleteForm.ts @@ -2,6 +2,7 @@ import { gettext } from "django"; import { customElement, html, property, TemplateResult } from "lit-element"; import { EVENT_REFRESH } from "../../constants"; import { ModalButton } from "../buttons/ModalButton"; +import { MessageLevel } from "../messages/Message"; import { showMessage } from "../messages/MessageContainer"; @customElement("ak-forms-delete") @@ -34,14 +35,14 @@ export class DeleteForm extends ModalButton { onSuccess(): void { showMessage({ message: gettext(`Successfully deleted ${this.objectLabel} ${ this.obj?.name }`), - level_tag: "success", + level: MessageLevel.success, }); } onError(e: Error): void { showMessage({ message: gettext(`Failed to delete ${this.objectLabel}: ${e.toString()}`), - level_tag: "error", + level: MessageLevel.error, }); } diff --git a/web/src/elements/messages/Message.ts b/web/src/elements/messages/Message.ts index 1c215f190..fddedd87c 100644 --- a/web/src/elements/messages/Message.ts +++ b/web/src/elements/messages/Message.ts @@ -5,10 +5,14 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css"; +export enum MessageLevel { + "error", "warning", "success", "info" +} export interface APIMessage { - level_tag: string; + level: MessageLevel; tags?: string; message: string; + description?: string; } const LEVEL_ICON_MAP: { [key: string]: string } = { @@ -44,13 +48,16 @@ export class Message extends LitElement { render(): TemplateResult { return html`
  • -
    +
    - +

    ${this.message?.message}

    + ${this.message?.description && html`
    +

    ${this.message.description}

    +
    `}