flows: inspector (#1469)
* flows: add initial inspector Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: change naming a bit Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/flow: add inspector frame Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: don't use shadydom when inspecting Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: add current stage to api Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * stages/*: fix imports Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: deep-copy plan instead of just adding Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/flows: ui Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: restrict inspector to admin Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/admin: add buttons to launch flow with inspector Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/flows: don't automatically follow redirects when inspector is open Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: make current_plan optional, only require historry Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/flows: handle error messages in inspector Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/flows: improve UI when flow is done Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: add is_completed flag to inspector Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: fix monkeypatches for tests Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * flows: add inspector tests Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * ci: re-enable cache Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
@ -14,6 +14,7 @@ export const EVENT_API_DRAWER_TOGGLE = "ak-api-drawer-toggle";
|
||||
export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle";
|
||||
export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh";
|
||||
export const EVENT_WS_MESSAGE = "ak-ws-message";
|
||||
export const EVENT_FLOW_ADVANCE = "ak-flow-advance";
|
||||
|
||||
export const WS_MSG_TYPE_MESSAGE = "message";
|
||||
export const WS_MSG_TYPE_REFRESH = "refresh";
|
||||
|
||||
@ -11,10 +11,10 @@ export class Expand extends LitElement {
|
||||
expanded = false;
|
||||
|
||||
@property()
|
||||
textOpen = "Show less";
|
||||
textOpen = t`Show less`;
|
||||
|
||||
@property()
|
||||
textClosed = "Show more";
|
||||
textClosed = t`Show more`;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFExpandableSection];
|
||||
|
||||
@ -8,6 +8,7 @@ import { until } from "lit/directives/until";
|
||||
import AKGlobal from "../authentik.css";
|
||||
import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
|
||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
@ -26,12 +27,14 @@ import {
|
||||
import { DEFAULT_CONFIG, tenant } from "../api/Config";
|
||||
import { configureSentry } from "../api/Sentry";
|
||||
import { WebsocketClient } from "../common/ws";
|
||||
import { TITLE_DEFAULT } from "../constants";
|
||||
import { EVENT_FLOW_ADVANCE, TITLE_DEFAULT } from "../constants";
|
||||
import "../elements/LoadingOverlay";
|
||||
import { DefaultTenant } from "../elements/sidebar/SidebarBrand";
|
||||
import { first } from "../utils";
|
||||
import "./FlowInspector";
|
||||
import "./access_denied/FlowAccessDenied";
|
||||
import "./sources/plex/PlexLoginInit";
|
||||
import "./stages/RedirectStage";
|
||||
import "./stages/authenticator_duo/AuthenticatorDuoStage";
|
||||
import "./stages/authenticator_static/AuthenticatorStaticStage";
|
||||
import "./stages/authenticator_totp/AuthenticatorTOTPStage";
|
||||
@ -59,7 +62,9 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
// Assign the location as soon as we get the challenge and *not* in the render function
|
||||
// as the render function might be called multiple times, which will navigate multiple
|
||||
// times and can invalidate oauth codes
|
||||
if (value?.type === ChallengeChoices.Redirect) {
|
||||
// Also only auto-redirect when the inspector is open, so that a user can inspect the
|
||||
// redirect in the inspector
|
||||
if (value?.type === ChallengeChoices.Redirect && !this.inspectorOpen) {
|
||||
console.debug(
|
||||
"authentik/flows: redirecting to url from server",
|
||||
(value as RedirectChallenge).to,
|
||||
@ -86,10 +91,14 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
@property({ attribute: false })
|
||||
tenant?: CurrentTenant;
|
||||
|
||||
@property({ attribute: false })
|
||||
inspectorOpen: boolean;
|
||||
|
||||
ws: WebsocketClient;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFLogin, PFButton, PFTitle, PFList, PFBackgroundImage, AKGlobal].concat(css`
|
||||
return [PFBase, PFLogin, PFDrawer, PFButton, PFTitle, PFList, PFBackgroundImage, AKGlobal]
|
||||
.concat(css`
|
||||
.ak-hidden {
|
||||
display: none;
|
||||
}
|
||||
@ -100,6 +109,9 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
font-family: monospace;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.pf-c-drawer__content {
|
||||
background-color: transparent;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
@ -107,6 +119,7 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
super();
|
||||
this.ws = new WebsocketClient();
|
||||
this.flowSlug = window.location.pathname.split("/")[3];
|
||||
this.inspectorOpen = window.location.search.includes("inspector");
|
||||
}
|
||||
|
||||
setBackground(url: string): void {
|
||||
@ -130,6 +143,14 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
flowChallengeResponseRequest: payload,
|
||||
})
|
||||
.then((data) => {
|
||||
if (this.inspectorOpen) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(EVENT_FLOW_ADVANCE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.challenge = data;
|
||||
})
|
||||
.catch((e: Error | Response) => {
|
||||
@ -150,6 +171,14 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
query: window.location.search.substring(1),
|
||||
})
|
||||
.then((challenge) => {
|
||||
if (this.inspectorOpen) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(EVENT_FLOW_ADVANCE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.challenge = challenge;
|
||||
// Only set background on first update, flow won't change throughout execution
|
||||
if (this.challenge?.flowInfo?.background) {
|
||||
@ -199,6 +228,13 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
}
|
||||
switch (this.challenge.type) {
|
||||
case ChallengeChoices.Redirect:
|
||||
if (this.inspectorOpen) {
|
||||
return html`<ak-stage-redirect
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
>
|
||||
</ak-stage-redirect>`;
|
||||
}
|
||||
return html`<ak-empty-state ?loading=${true} header=${t`Loading`}>
|
||||
</ak-empty-state>`;
|
||||
case ChallengeChoices.Shell:
|
||||
@ -333,50 +369,74 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
</filter>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="pf-c-login">
|
||||
<div class="ak-login-container">
|
||||
<header class="pf-c-login__header">
|
||||
<div class="pf-c-brand ak-brand">
|
||||
<img
|
||||
src="${first(
|
||||
this.tenant?.brandingLogo,
|
||||
DefaultTenant.brandingLogo,
|
||||
)}"
|
||||
alt="authentik icon"
|
||||
/>
|
||||
<div class="pf-c-page__drawer">
|
||||
<div class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
|
||||
<div class="pf-c-drawer__main">
|
||||
<div class="pf-c-drawer__content">
|
||||
<div class="pf-c-drawer__body">
|
||||
<div class="pf-c-login">
|
||||
<div class="ak-login-container">
|
||||
<header class="pf-c-login__header">
|
||||
<div class="pf-c-brand ak-brand">
|
||||
<img
|
||||
src="${first(
|
||||
this.tenant?.brandingLogo,
|
||||
DefaultTenant.brandingLogo,
|
||||
)}"
|
||||
alt="authentik icon"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<div class="pf-c-login__main">
|
||||
${this.renderChallengeWrapper()}
|
||||
</div>
|
||||
<footer class="pf-c-login__footer">
|
||||
<p></p>
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
${until(
|
||||
this.tenant?.uiFooterLinks?.map((link) => {
|
||||
return html`<li>
|
||||
<a href="${link.href || ""}"
|
||||
>${link.name}</a
|
||||
>
|
||||
</li>`;
|
||||
}),
|
||||
)}
|
||||
${this.tenant?.brandingTitle != "authentik"
|
||||
? html`
|
||||
<li>
|
||||
<a href="https://goauthentik.io"
|
||||
>${t`Powered by authentik`}</a
|
||||
>
|
||||
</li>
|
||||
`
|
||||
: html``}
|
||||
${this.challenge?.flowInfo?.background?.startsWith(
|
||||
"/static",
|
||||
)
|
||||
? html`
|
||||
<li>
|
||||
<a
|
||||
href="https://unsplash.com/@introspectivedsgn"
|
||||
>${t`Background image`}</a
|
||||
>
|
||||
</li>
|
||||
`
|
||||
: html``}
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="pf-c-login__main">${this.renderChallengeWrapper()}</div>
|
||||
<footer class="pf-c-login__footer">
|
||||
<p></p>
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
${until(
|
||||
this.tenant?.uiFooterLinks?.map((link) => {
|
||||
return html`<li>
|
||||
<a href="${link.href || ""}">${link.name}</a>
|
||||
</li>`;
|
||||
}),
|
||||
)}
|
||||
${this.tenant?.brandingTitle != "authentik"
|
||||
? html`
|
||||
<li>
|
||||
<a href="https://goauthentik.io"
|
||||
>${t`Powered by authentik`}</a
|
||||
>
|
||||
</li>
|
||||
`
|
||||
: html``}
|
||||
${this.challenge?.flowInfo?.background?.startsWith("/static")
|
||||
? html`
|
||||
<li>
|
||||
<a href="https://unsplash.com/@introspectivedsgn"
|
||||
>${t`Background image`}</a
|
||||
>
|
||||
</li>
|
||||
`
|
||||
: html``}
|
||||
</ul>
|
||||
</footer>
|
||||
|
||||
<ak-flow-inspector
|
||||
class="pf-c-drawer__panel pf-m-width-33 ${this.inspectorOpen
|
||||
? ""
|
||||
: "display-none"}"
|
||||
?hidden=${!this.inspectorOpen}
|
||||
></ak-flow-inspector>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
297
web/src/flows/FlowInspector.ts
Normal file
297
web/src/flows/FlowInspector.ts
Normal file
@ -0,0 +1,297 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../authentik.css";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css";
|
||||
import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";
|
||||
import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { FlowInspection, FlowsApi, Stage } from "@goauthentik/api";
|
||||
|
||||
import { DEFAULT_CONFIG } from "../api/Config";
|
||||
import { EVENT_FLOW_ADVANCE } from "../constants";
|
||||
import "../elements/Expand";
|
||||
|
||||
@customElement("ak-flow-inspector")
|
||||
export class FlowInspector extends LitElement {
|
||||
flowSlug: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
state?: FlowInspection;
|
||||
|
||||
@property({ attribute: false })
|
||||
error?: Response;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFStack,
|
||||
PFCard,
|
||||
PFNotificationDrawer,
|
||||
PFDescriptionList,
|
||||
PFProgressStepper,
|
||||
AKGlobal,
|
||||
css`
|
||||
code.break {
|
||||
word-break: break-all;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.flowSlug = window.location.pathname.split("/")[3];
|
||||
window.addEventListener(EVENT_FLOW_ADVANCE, this.advanceHandler as EventListener);
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener(EVENT_FLOW_ADVANCE, this.advanceHandler as EventListener);
|
||||
}
|
||||
|
||||
advanceHandler = (): void => {
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInspectorGet({
|
||||
flowSlug: this.flowSlug,
|
||||
})
|
||||
.then((state) => {
|
||||
this.state = state;
|
||||
})
|
||||
.catch((exc) => {
|
||||
this.error = exc;
|
||||
});
|
||||
};
|
||||
|
||||
// getStage return a stage without flowSet, for brevity
|
||||
getStage(stage?: Stage): unknown {
|
||||
if (!stage) {
|
||||
return stage;
|
||||
}
|
||||
delete stage.flowSet;
|
||||
return stage;
|
||||
}
|
||||
|
||||
renderAccessDenied(): TemplateResult {
|
||||
return html`<div class="pf-c-drawer__body pf-m-no-padding">
|
||||
<div class="pf-c-notification-drawer">
|
||||
<div class="pf-c-notification-drawer__header">
|
||||
<div class="text">
|
||||
<h1 class="pf-c-notification-drawer__header-title">${t`Flow inspector`}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-notification-drawer__body">
|
||||
<div class="pf-l-stack pf-m-gutter">
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">${this.error?.statusText}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (this.error) {
|
||||
return this.renderAccessDenied();
|
||||
}
|
||||
if (!this.state) {
|
||||
return html`<ak-empty-state ?loading="${true}" header=${t`Loading`}> </ak-empty-state>`;
|
||||
}
|
||||
return html`<div class="pf-c-drawer__body pf-m-no-padding">
|
||||
<div class="pf-c-notification-drawer">
|
||||
<div class="pf-c-notification-drawer__header">
|
||||
<div class="text">
|
||||
<h1 class="pf-c-notification-drawer__header-title">${t`Flow inspector`}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-notification-drawer__body">
|
||||
<div class="pf-l-stack pf-m-gutter">
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">${t`Next stage`}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Stage name`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.state.currentPlan?.nextPlannedStage
|
||||
?.stageObj?.name || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Stage kind`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.state.currentPlan?.nextPlannedStage
|
||||
?.stageObj?.verboseName || "-"}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${t`Stage object`}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
${this.state.isCompleted
|
||||
? html` <div
|
||||
class="pf-c-description-list__text"
|
||||
>
|
||||
${t`This flow is completed.`}
|
||||
</div>`
|
||||
: html`<ak-expand>
|
||||
<pre class="pf-c-description-list__text">
|
||||
${JSON.stringify(this.getStage(this.state.currentPlan?.nextPlannedStage?.stageObj), null, 4)}</pre
|
||||
>
|
||||
</ak-expand>`}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">${t`Plan history`}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ol class="pf-c-progress-stepper pf-m-vertical">
|
||||
${this.state.plans.map((plan) => {
|
||||
return html`<li
|
||||
class="pf-c-progress-stepper__step pf-m-success"
|
||||
>
|
||||
<div class="pf-c-progress-stepper__step-connector">
|
||||
<span class="pf-c-progress-stepper__step-icon">
|
||||
<i
|
||||
class="fas fa-check-circle"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div class="pf-c-progress-stepper__step-title">
|
||||
${plan.currentStage.stageObj?.name}
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-description"
|
||||
>
|
||||
${plan.currentStage.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li> `;
|
||||
})}
|
||||
${this.state.currentPlan?.currentStage &&
|
||||
!this.state.isCompleted
|
||||
? html` <li
|
||||
class="pf-c-progress-stepper__step pf-m-current pf-m-info"
|
||||
>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-connector"
|
||||
>
|
||||
<span
|
||||
class="pf-c-progress-stepper__step-icon"
|
||||
>
|
||||
<i
|
||||
class="pficon pf-icon-resources-full"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-title"
|
||||
>
|
||||
${this.state.currentPlan?.currentStage
|
||||
?.stageObj?.name}
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-description"
|
||||
>
|
||||
${this.state.currentPlan?.currentStage
|
||||
?.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li>`
|
||||
: html``}
|
||||
${this.state.currentPlan?.nextPlannedStage &&
|
||||
!this.state.isCompleted
|
||||
? html`<li
|
||||
class="pf-c-progress-stepper__step pf-m-pending"
|
||||
>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-connector"
|
||||
>
|
||||
<span
|
||||
class="pf-c-progress-stepper__step-icon"
|
||||
></span>
|
||||
</div>
|
||||
<div class="pf-c-progress-stepper__step-main">
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-title"
|
||||
>
|
||||
${this.state.currentPlan.nextPlannedStage
|
||||
.stageObj?.name}
|
||||
</div>
|
||||
<div
|
||||
class="pf-c-progress-stepper__step-description"
|
||||
>
|
||||
${this.state.currentPlan?.nextPlannedStage
|
||||
?.stageObj?.verboseName}
|
||||
</div>
|
||||
</div>
|
||||
</li>`
|
||||
: html``}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">${t`Current plan cntext`}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<pre>
|
||||
${JSON.stringify(this.state.currentPlan?.planContext, null, 4)}</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-l-stack__item">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header">
|
||||
<div class="pf-c-card__title">${t`Session ID`}</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<code class="break">${this.state.currentPlan?.sessionId}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
56
web/src/flows/stages/RedirectStage.ts
Normal file
56
web/src/flows/stages/RedirectStage.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../../authentik.css";
|
||||
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";
|
||||
|
||||
import { FlowChallengeResponseRequest, RedirectChallenge } from "@goauthentik/api";
|
||||
|
||||
import { BaseStage } from "./base";
|
||||
|
||||
@customElement("ak-stage-redirect")
|
||||
export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeResponseRequest> {
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFLogin, PFForm, PFButton, PFFormControl, PFTitle, AKGlobal];
|
||||
}
|
||||
|
||||
renderURL(): string {
|
||||
if (!this.challenge.to.includes("://")) {
|
||||
return window.location.origin + this.challenge.to;
|
||||
}
|
||||
return this.challenge.to;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">${t`Redirect`}</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form method="POST" class="pf-c-form">
|
||||
<div class="pf-c-form__group">
|
||||
<p>${t`You're about to be redirect to the following URL.`}</p>
|
||||
<pre>${this.renderURL()}</pre>
|
||||
</div>
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<a
|
||||
type="submit"
|
||||
class="pf-c-button pf-m-primary pf-m-block"
|
||||
href=${this.challenge.to}
|
||||
>
|
||||
${t`Follow redirect`}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<footer class="pf-c-login__main-footer">
|
||||
<ul class="pf-c-login__main-footer-links"></ul>
|
||||
</footer> `;
|
||||
}
|
||||
}
|
||||
@ -1151,6 +1151,10 @@ msgstr "Created {0}"
|
||||
msgid "Creation Date"
|
||||
msgstr "Creation Date"
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Current plan cntext"
|
||||
msgstr "Current plan cntext"
|
||||
|
||||
#: src/pages/applications/ApplicationForm.ts
|
||||
#: src/pages/flows/FlowForm.ts
|
||||
msgid "Currently set to:"
|
||||
@ -1626,6 +1630,10 @@ msgstr "Execute"
|
||||
msgid "Execute flow"
|
||||
msgstr "Execute flow"
|
||||
|
||||
#: src/pages/flows/FlowViewPage.ts
|
||||
msgid "Execute with inspector"
|
||||
msgstr "Execute with inspector"
|
||||
|
||||
#: src/pages/policies/expression/ExpressionPolicyForm.ts
|
||||
msgid "Executes the python snippet to determine whether to allow or deny a request."
|
||||
msgstr "Executes the python snippet to determine whether to allow or deny a request."
|
||||
@ -1793,6 +1801,11 @@ msgstr "Flow"
|
||||
msgid "Flow Overview"
|
||||
msgstr "Flow Overview"
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Flow inspector"
|
||||
msgstr "Flow inspector"
|
||||
|
||||
#: src/pages/sources/oauth/OAuthSourceForm.ts
|
||||
#: src/pages/sources/plex/PlexSourceForm.ts
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
@ -1860,6 +1873,10 @@ msgstr "Flows"
|
||||
msgid "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them."
|
||||
msgstr "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them."
|
||||
|
||||
#: src/flows/stages/RedirectStage.ts
|
||||
msgid "Follow redirect"
|
||||
msgstr "Follow redirect"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Force the user to configure an authenticator"
|
||||
msgstr "Force the user to configure an authenticator"
|
||||
@ -2350,6 +2367,7 @@ msgstr "Load servers"
|
||||
#: src/elements/table/Table.ts
|
||||
#: src/flows/FlowExecutor.ts
|
||||
#: src/flows/FlowExecutor.ts
|
||||
#: src/flows/FlowInspector.ts
|
||||
#: src/flows/access_denied/FlowAccessDenied.ts
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
|
||||
@ -2714,6 +2732,10 @@ msgstr "New version available!"
|
||||
msgid "Newly created users are added to this group, if a group is selected."
|
||||
msgstr "Newly created users are added to this group, if a group is selected."
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Next stage"
|
||||
msgstr "Next stage"
|
||||
|
||||
#: src/elements/oauth/UserRefreshList.ts
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/crypto/CertificateKeyPairListPage.ts
|
||||
@ -3079,6 +3101,10 @@ msgstr "Persistent"
|
||||
msgid "Placeholder"
|
||||
msgstr "Placeholder"
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Plan history"
|
||||
msgstr "Plan history"
|
||||
|
||||
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Please enter your TOTP Code"
|
||||
@ -3381,6 +3407,7 @@ msgstr "Recovery keys"
|
||||
msgid "Recovery link cannot be emailed, user has no email address saved."
|
||||
msgstr "Recovery link cannot be emailed, user has no email address saved."
|
||||
|
||||
#: src/flows/stages/RedirectStage.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Redirect"
|
||||
msgstr "Redirect"
|
||||
@ -3748,6 +3775,10 @@ msgstr "Service Provider Binding"
|
||||
#~ msgid "Session"
|
||||
#~ msgstr "Session"
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Session ID"
|
||||
msgstr "Session ID"
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
msgid "Session duration"
|
||||
msgstr "Session duration"
|
||||
@ -3794,10 +3825,18 @@ msgstr "Severity"
|
||||
msgid "Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable."
|
||||
msgstr "Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable."
|
||||
|
||||
#: src/elements/Expand.ts
|
||||
msgid "Show less"
|
||||
msgstr "Show less"
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "Show matched user"
|
||||
msgstr "Show matched user"
|
||||
|
||||
#: src/elements/Expand.ts
|
||||
msgid "Show more"
|
||||
msgstr "Show more"
|
||||
|
||||
#: src/pages/flows/FlowForm.ts
|
||||
msgid "Shown as the Title in Flow pages."
|
||||
msgstr "Shown as the Title in Flow pages."
|
||||
@ -3897,6 +3936,18 @@ msgstr "Stage Configuration"
|
||||
msgid "Stage binding(s)"
|
||||
msgstr "Stage binding(s)"
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Stage kind"
|
||||
msgstr "Stage kind"
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Stage name"
|
||||
msgstr "Stage name"
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Stage object"
|
||||
msgstr "Stage object"
|
||||
|
||||
#: src/pages/flows/BoundStagesList.ts
|
||||
msgid "Stage type"
|
||||
msgstr "Stage type"
|
||||
@ -4505,6 +4556,10 @@ msgstr ""
|
||||
msgid "These policies control which users can access this application."
|
||||
msgstr "These policies control which users can access this application."
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "This flow is completed."
|
||||
msgstr "This flow is completed."
|
||||
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
msgid "This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well."
|
||||
msgstr "This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well."
|
||||
@ -5276,6 +5331,10 @@ msgstr "Yes"
|
||||
msgid "You can only select providers that match the type of the outpost."
|
||||
msgstr "You can only select providers that match the type of the outpost."
|
||||
|
||||
#: src/flows/stages/RedirectStage.ts
|
||||
msgid "You're about to be redirect to the following URL."
|
||||
msgstr "You're about to be redirect to the following URL."
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "You're currently impersonating {0}. Click to stop."
|
||||
msgstr "You're currently impersonating {0}. Click to stop."
|
||||
|
||||
@ -1145,6 +1145,10 @@ msgstr ""
|
||||
msgid "Creation Date"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Current plan cntext"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/applications/ApplicationForm.ts
|
||||
#: src/pages/flows/FlowForm.ts
|
||||
msgid "Currently set to:"
|
||||
@ -1618,6 +1622,10 @@ msgstr ""
|
||||
msgid "Execute flow"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/flows/FlowViewPage.ts
|
||||
msgid "Execute with inspector"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/policies/expression/ExpressionPolicyForm.ts
|
||||
msgid "Executes the python snippet to determine whether to allow or deny a request."
|
||||
msgstr ""
|
||||
@ -1785,6 +1793,11 @@ msgstr ""
|
||||
msgid "Flow Overview"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Flow inspector"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/oauth/OAuthSourceForm.ts
|
||||
#: src/pages/sources/plex/PlexSourceForm.ts
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
@ -1852,6 +1865,10 @@ msgstr ""
|
||||
msgid "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them."
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/stages/RedirectStage.ts
|
||||
msgid "Follow redirect"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Force the user to configure an authenticator"
|
||||
msgstr ""
|
||||
@ -2342,6 +2359,7 @@ msgstr ""
|
||||
#: src/elements/table/Table.ts
|
||||
#: src/flows/FlowExecutor.ts
|
||||
#: src/flows/FlowExecutor.ts
|
||||
#: src/flows/FlowInspector.ts
|
||||
#: src/flows/access_denied/FlowAccessDenied.ts
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
|
||||
@ -2706,6 +2724,10 @@ msgstr ""
|
||||
msgid "Newly created users are added to this group, if a group is selected."
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Next stage"
|
||||
msgstr ""
|
||||
|
||||
#: src/elements/oauth/UserRefreshList.ts
|
||||
#: src/pages/applications/ApplicationCheckAccessForm.ts
|
||||
#: src/pages/crypto/CertificateKeyPairListPage.ts
|
||||
@ -3071,6 +3093,10 @@ msgstr ""
|
||||
msgid "Placeholder"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Plan history"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Please enter your TOTP Code"
|
||||
@ -3373,6 +3399,7 @@ msgstr ""
|
||||
msgid "Recovery link cannot be emailed, user has no email address saved."
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/stages/RedirectStage.ts
|
||||
#: src/pages/providers/saml/SAMLProviderForm.ts
|
||||
msgid "Redirect"
|
||||
msgstr ""
|
||||
@ -3740,6 +3767,10 @@ msgstr ""
|
||||
#~ msgid "Session"
|
||||
#~ msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Session ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||
msgid "Session duration"
|
||||
msgstr ""
|
||||
@ -3786,10 +3817,18 @@ msgstr ""
|
||||
msgid "Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable."
|
||||
msgstr ""
|
||||
|
||||
#: src/elements/Expand.ts
|
||||
msgid "Show less"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "Show matched user"
|
||||
msgstr ""
|
||||
|
||||
#: src/elements/Expand.ts
|
||||
msgid "Show more"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/flows/FlowForm.ts
|
||||
msgid "Shown as the Title in Flow pages."
|
||||
msgstr ""
|
||||
@ -3889,6 +3928,18 @@ msgstr ""
|
||||
msgid "Stage binding(s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Stage kind"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Stage name"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "Stage object"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/flows/BoundStagesList.ts
|
||||
msgid "Stage type"
|
||||
msgstr ""
|
||||
@ -4490,6 +4541,10 @@ msgstr ""
|
||||
msgid "These policies control which users can access this application."
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowInspector.ts
|
||||
msgid "This flow is completed."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
msgid "This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well."
|
||||
msgstr ""
|
||||
@ -5259,6 +5314,10 @@ msgstr ""
|
||||
msgid "You can only select providers that match the type of the outpost."
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/stages/RedirectStage.ts
|
||||
msgid "You're about to be redirect to the following URL."
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "You're currently impersonating {0}. Click to stop."
|
||||
msgstr ""
|
||||
|
||||
@ -104,7 +104,9 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
slug: item.slug,
|
||||
})
|
||||
.then((link) => {
|
||||
window.open(`${link.link}?next=/%23${window.location.href}`);
|
||||
window.open(
|
||||
`${link.link}?inspector&next=/%23${window.location.href}`,
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@ -107,6 +107,21 @@ export class FlowViewPage extends LitElement {
|
||||
>
|
||||
${t`Execute`}
|
||||
</button>
|
||||
<button
|
||||
class="pf-c-button pf-m-secondary"
|
||||
@click=${() => {
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesExecuteRetrieve({
|
||||
slug: this.flow.slug,
|
||||
})
|
||||
.then((link) => {
|
||||
const finalURL = `${link.link}?inspector&next=/%23${window.location.hash}`;
|
||||
window.open(finalURL, "_blank");
|
||||
});
|
||||
}}
|
||||
>
|
||||
${t`Execute with inspector`}
|
||||
</button>
|
||||
</div>
|
||||
</dd>
|
||||
<dt class="pf-c-description-list__term">
|
||||
|
||||
Reference in New Issue
Block a user