Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 78 additions & 12 deletions web/src/flow/FormStatic.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
import { AKElement } from "#elements/Base";
import { LitFC } from "#elements/types";
import { ifPresent } from "#elements/utils/attributes";
import { isDefaultAvatar } from "#elements/utils/images";

import {
AccessDeniedChallenge,
AuthenticatorDuoChallenge,
AuthenticatorEmailChallenge,
AuthenticatorStaticChallenge,
AuthenticatorTOTPChallenge,
AuthenticatorWebAuthnChallenge,
CaptchaChallenge,
ConsentChallenge,
PasswordChallenge,
SessionEndChallenge,
UserLoginChallenge,
} from "@goauthentik/api";

import { msg, str } from "@lit/localize";
import { css, CSSResult, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { guard } from "lit/directives/guard.js";

import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";

@customElement("ak-form-static")
export class FormStatic extends AKElement {
export class AKFormStatic extends AKElement {
public override role = "banner";
public override ariaLabel = msg("User information");

@property()
userAvatar?: string;
@property({ type: String })
public avatar: string = "";

@property()
user?: string;
@property({ type: String })
public username: string = "";

static styles: CSSResult[] = [
PFAvatar,
Expand Down Expand Up @@ -64,21 +81,27 @@ export class FormStatic extends AKElement {
`,
];

render() {
if (!this.user) {
protected override render() {
if (!this.username) {
return nothing;
}

return html`
<div class="primary-content">
${this.userAvatar && !isDefaultAvatar(this.userAvatar)
${this.avatar && !isDefaultAvatar(this.avatar)
? html`<img
class="pf-c-avatar"
src=${this.userAvatar}
alt=${this.user ? msg(str`Avatar for ${this.user}`) : msg("User avatar")}
src=${this.avatar}
alt=${this.username
? msg(str`Avatar for ${this.username}`, {
id: "avatar.alt-text-for-user",
})
: msg("User avatar", {
id: "avatar.alt-text",
})}
/>`
: nothing}
<div class="username" aria-description=${msg("Username")}>${this.user}</div>
<div class="username" aria-description=${msg("Username")}>${this.username}</div>
</div>
<div class="links">
<slot name="link"></slot>
Expand All @@ -87,8 +110,51 @@ export class FormStatic extends AKElement {
}
}

/**
* @internal
*/
export type FormStaticChallenge =
| SessionEndChallenge
| AccessDeniedChallenge
| AuthenticatorDuoChallenge
| AuthenticatorEmailChallenge
| AuthenticatorStaticChallenge
| AuthenticatorTOTPChallenge
| AuthenticatorWebAuthnChallenge
| CaptchaChallenge
| ConsentChallenge
| PasswordChallenge
| UserLoginChallenge;

export interface FlowUserDetailsProps {
challenge?: Partial<
Pick<FormStaticChallenge, "pendingUserAvatar" | "pendingUser" | "flowInfo">
>;
}

export const FlowUserDetails: LitFC<FlowUserDetailsProps> = ({ challenge }) => {
const { pendingUserAvatar, pendingUser, flowInfo } = challenge || {};
return guard(
[pendingUserAvatar, pendingUser, flowInfo],
() =>
html`<ak-form-static
class="pf-c-form__group"
avatar=${ifPresent(pendingUserAvatar)}
username=${ifPresent(pendingUser)}
>
${flowInfo?.cancelUrl
? html`
<div slot="link">
<a href=${flowInfo.cancelUrl}>${msg("Not you?")}</a>
</div>
`
: nothing}
</ak-form-static>`,
);
};

declare global {
interface HTMLElementTagNameMap {
"ak-form-static": FormStatic;
"ak-form-static": AKFormStatic;
}
}
15 changes: 3 additions & 12 deletions web/src/flow/providers/SessionEnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import "#flow/components/ak-flow-card";

import { globalAK } from "#common/global";

import { FlowUserDetails } from "#flow/FormStatic";
import { BaseStage } from "#flow/stages/base";

import { SessionEndChallenge } from "@goauthentik/api";

import { msg, str } from "@lit/localize";
import { CSSResult, html, nothing, TemplateResult } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";

import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
Expand All @@ -26,17 +26,8 @@ export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
render(): TemplateResult {
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
${FlowUserDetails({ challenge: this.challenge })}

<p>
${msg(
str`You've logged out of ${this.challenge.applicationName}. You can go back to the overview to launch another application, or log out of your authentik account.`,
Expand Down
39 changes: 25 additions & 14 deletions web/src/flow/stages/access_denied/AccessDeniedStage.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
import "#flow/FormStatic";
import "#flow/components/ak-flow-card";

import { FlowUserDetails } from "#flow/FormStatic";
import { BaseStage } from "#flow/stages/base";

import { AccessDeniedChallenge, FlowChallengeResponseRequest } from "@goauthentik/api";

import { msg } from "@lit/localize";
import { CSSResult, html, nothing, TemplateResult } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";

import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";

@customElement("ak-stage-access-denied")
export class AccessDeniedStage extends BaseStage<
AccessDeniedChallenge,
FlowChallengeResponseRequest
> {
static styles: CSSResult[] = [PFBase, PFLogin, PFForm, PFTitle, PFFormControl];
static styles: CSSResult[] = [
// ---
PFLogin,
PFForm,
PFTitle,
PFFormControl,
PFButton,
];

render(): TemplateResult {
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge?.pendingUserAvatar}"
user=${this.challenge?.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
${FlowUserDetails({ challenge: this.challenge })}
<ak-empty-state icon="fa-times"
><span>${msg("Request has been denied.")}</span>
${this.challenge.errorMessage
Expand All @@ -47,6 +44,20 @@ export class AccessDeniedStage extends BaseStage<
`
: nothing}
</ak-empty-state>
${this.challenge.flowInfo?.cancelUrl
? html`<fieldset class="pf-c-form__group pf-m-action">
<legend class="sr-only">${msg("Form actions")}</legend>
<a
class="pf-c-button pf-m-primary pf-m-block"
href=${this.challenge.flowInfo?.cancelUrl}
>
${msg("Go back", {
id: "flow.navigation.go-back",
})}
</a>
</fieldset>`
: nothing}
}
</form>
</ak-flow-card>`;
}
Expand Down
15 changes: 3 additions & 12 deletions web/src/flow/stages/authenticator_duo/AuthenticatorDuoStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import "#flow/components/ak-flow-card";

import { DEFAULT_CONFIG } from "#common/api/config";

import { FlowUserDetails } from "#flow/FormStatic";
import { BaseStage } from "#flow/stages/base";

import {
Expand All @@ -15,7 +16,6 @@ import {
import { msg } from "@lit/localize";
import { CSSResult, html, PropertyValues, TemplateResult } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";

import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
Expand Down Expand Up @@ -67,17 +67,8 @@ export class AuthenticatorDuoStage extends BaseStage<
render(): TemplateResult {
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form" @submit=${this.submitForm}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
${FlowUserDetails({ challenge: this.challenge })}

<img src=${this.challenge.activationBarcode} alt=${msg("Duo activation QR code")} />
<p>
${msg(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import "#flow/components/ak-flow-card";
import { AKFormErrors } from "#components/ak-field-errors";
import { AKLabel } from "#components/ak-label";

import { FlowUserDetails } from "#flow/FormStatic";
import { BaseStage } from "#flow/stages/base";

import {
AuthenticatorEmailChallenge,
AuthenticatorEmailChallengeResponseRequest,
} from "@goauthentik/api";

import { msg } from "@lit/localize";
import { msg, str } from "@lit/localize";
import { CSSResult, html, TemplateResult } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";

import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
Expand Down Expand Up @@ -44,17 +44,8 @@ export class AuthenticatorEmailStage extends BaseStage<
renderEmailInput(): TemplateResult {
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form" @submit=${this.submitForm}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
${FlowUserDetails({ challenge: this.challenge })}

<div class="pf-c-form__group">
${AKLabel(
{ required: true, htmlFor: "email-input" },
Expand Down Expand Up @@ -88,20 +79,25 @@ export class AuthenticatorEmailStage extends BaseStage<
}

renderEmailOTPInput(): TemplateResult {
const { email } = this.challenge;

return html`<ak-flow-card .challenge=${this.challenge}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
A verification token has been sent to your configured email address
${ifDefined(this.challenge.email)}
${FlowUserDetails({ challenge: this.challenge })}

<p>
${email
? msg(
str`A verification token has been sent to your configured email address: ${email}`,
{
id: "stage.authenticator.email.sent-to-address",
desc: "Displayed when a verification token has been sent to the user's configured email address.",
},
)
: msg("A verification token has been sent to your email address.", {
id: "stage.authenticator.email.sent",
desc: "Displayed when a verification token has been sent to the user's email address.",
})}
</p>
<form class="pf-c-form" @submit=${this.submitForm}>
<div class="pf-c-form__group">
${AKLabel({ required: true, htmlFor: "code-input" }, msg("Code"))}
Expand Down
Loading
Loading