web: use generated API Client (#616)
* api: fix types for config API * api: remove broken swagger UI * admin: re-fix system task enum * events: make event optional * events: fix Schema for notification transport test * flows: use APIView for Flow Executor * core: fix schema for Metrics APIs * web: rewrite to use generated API client * web: generate API Client in CI * admin: use x_cord and y_cord to prevent yaml issues * events: fix linting errors * web: don't lint generated code * core: fix fields not being required in TypeSerializer * flows: fix missing permission_classes * web: cleanup * web: fix rendering of graph on Overview page * web: cleanup imports * core: fix missing background image filter * flows: fix flows not advancing properly * stages/*: fix warnings during get_challenge * web: send Flow response as JSON instead of FormData * web: fix styles for horizontal tabs * web: add base chart class and custom chart for application view * root: generate ts client for e2e tests * web: don't attempt to connect to websocket in selenium tests * web: fix UserTokenList not being included in the build * web: fix styling for static token list * web: fix CSRF Token missing * stages/authenticator_static: fix error when disable static tokens * core: fix display issue when updating user info * web: fix Flow executor not showing spinner when redirecting
This commit is contained in:
		
							
								
								
									
										185
									
								
								web/src/flows/FlowExecutor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								web/src/flows/FlowExecutor.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | ||||
| import { gettext } from "django"; | ||||
| import { LitElement, html, customElement, property, TemplateResult, CSSResult, css } from "lit-element"; | ||||
| import { unsafeHTML } from "lit-html/directives/unsafe-html"; | ||||
| import "./stages/authenticator_static/AuthenticatorStaticStage"; | ||||
| import "./stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||
| import "./stages/authenticator_validate/AuthenticatorValidateStage"; | ||||
| import "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||
| import "./stages/autosubmit/AutosubmitStage"; | ||||
| import "./stages/captcha/CaptchaStage"; | ||||
| import "./stages/consent/ConsentStage"; | ||||
| import "./stages/email/EmailStage"; | ||||
| import "./stages/identification/IdentificationStage"; | ||||
| import "./stages/password/PasswordStage"; | ||||
| import "./stages/prompt/PromptStage"; | ||||
| import { ShellChallenge, RedirectChallenge } from "../api/Flows"; | ||||
| import { IdentificationChallenge } from "./stages/identification/IdentificationStage"; | ||||
| import { PasswordChallenge } from "./stages/password/PasswordStage"; | ||||
| import { ConsentChallenge } from "./stages/consent/ConsentStage"; | ||||
| import { EmailChallenge } from "./stages/email/EmailStage"; | ||||
| import { AutosubmitChallenge } from "./stages/autosubmit/AutosubmitStage"; | ||||
| import { PromptChallenge } from "./stages/prompt/PromptStage"; | ||||
| import { AuthenticatorTOTPChallenge } from "./stages/authenticator_totp/AuthenticatorTOTPStage"; | ||||
| import { AuthenticatorStaticChallenge } from "./stages/authenticator_static/AuthenticatorStaticStage"; | ||||
| import { AuthenticatorValidateStageChallenge } from "./stages/authenticator_validate/AuthenticatorValidateStage"; | ||||
| import { WebAuthnAuthenticatorRegisterChallenge } from "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||
| import { CaptchaChallenge } from "./stages/captcha/CaptchaStage"; | ||||
| import { COMMON_STYLES } from "../common/styles"; | ||||
| import { SpinnerSize } from "../elements/Spinner"; | ||||
| import { StageHost } from "./stages/base"; | ||||
| import { Challenge, ChallengeTypeEnum, FlowsApi } from "../api"; | ||||
| import { DEFAULT_CONFIG } from "../api/Config"; | ||||
|  | ||||
| @customElement("ak-flow-executor") | ||||
| export class FlowExecutor extends LitElement implements StageHost { | ||||
|     @property() | ||||
|     flowSlug = ""; | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     challenge?: Challenge; | ||||
|  | ||||
|     @property({type: Boolean}) | ||||
|     loading = false; | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return COMMON_STYLES.concat(css` | ||||
|             .ak-loading { | ||||
|                 display: flex; | ||||
|                 height: 100%; | ||||
|                 width: 100%; | ||||
|                 justify-content: center; | ||||
|                 align-items: center; | ||||
|                 position: absolute; | ||||
|                 background-color: #0303039e; | ||||
|             } | ||||
|             .ak-hidden { | ||||
|                 display: none; | ||||
|             } | ||||
|             :host { | ||||
|                 position: relative; | ||||
|             } | ||||
|         `); | ||||
|     } | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         this.addEventListener("ak-flow-submit", () => { | ||||
|             this.submit(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     submit<T>(formData?: T): Promise<void> { | ||||
|         this.loading = true; | ||||
|         return new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolveRaw({ | ||||
|             flowSlug: this.flowSlug, | ||||
|             data: formData || {}, | ||||
|         }).then((challengeRaw) => { | ||||
|             return challengeRaw.raw.json(); | ||||
|         }).then((data) => { | ||||
|             this.challenge = data; | ||||
|         }).catch((e) => { | ||||
|             this.errorMessage(e); | ||||
|         }).finally(() => { | ||||
|             this.loading = false; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         this.loading = true; | ||||
|         new FlowsApi(DEFAULT_CONFIG).flowsExecutorGetRaw({ | ||||
|             flowSlug: this.flowSlug | ||||
|         }).then((challengeRaw) => { | ||||
|             return challengeRaw.raw.json(); | ||||
|         }).then((challenge) => { | ||||
|             this.challenge = challenge as Challenge; | ||||
|         }).catch((e) => { | ||||
|             // Catch JSON or Update errors | ||||
|             this.errorMessage(e); | ||||
|         }).finally(() => { | ||||
|             this.loading = false; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     errorMessage(error: string): void { | ||||
|         this.challenge = <ShellChallenge>{ | ||||
|             type: ChallengeTypeEnum.Shell, | ||||
|             body: `<style> | ||||
|                     .ak-exception { | ||||
|                         font-family: monospace; | ||||
|                         overflow-x: scroll; | ||||
|                     } | ||||
|                 </style> | ||||
|                 <header class="pf-c-login__main-header"> | ||||
|                     <h1 class="pf-c-title pf-m-3xl"> | ||||
|                         ${gettext("Whoops!")} | ||||
|                     </h1> | ||||
|                 </header> | ||||
|                 <div class="pf-c-login__main-body"> | ||||
|                     <h3>${gettext("Something went wrong! Please try again later.")}</h3> | ||||
|                     <pre class="ak-exception">${error}</pre> | ||||
|                 </div>` | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     renderLoading(): TemplateResult { | ||||
|         return html`<div class="ak-loading"> | ||||
|             <ak-spinner size=${SpinnerSize.XLarge}></ak-spinner> | ||||
|         </div>`; | ||||
|     } | ||||
|  | ||||
|     renderChallenge(): TemplateResult { | ||||
|         if (!this.challenge) { | ||||
|             return this.renderLoading(); | ||||
|         } | ||||
|         switch (this.challenge.type) { | ||||
|             case ChallengeTypeEnum.Redirect: | ||||
|                 console.debug(`authentik/flows: redirecting to ${(this.challenge as RedirectChallenge).to}`); | ||||
|                 window.location.assign((this.challenge as RedirectChallenge).to); | ||||
|                 return this.renderLoading(); | ||||
|             case ChallengeTypeEnum.Shell: | ||||
|                 return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`; | ||||
|             case ChallengeTypeEnum.Native: | ||||
|                 switch (this.challenge.component) { | ||||
|                     case "ak-stage-identification": | ||||
|                         return html`<ak-stage-identification .host=${this} .challenge=${this.challenge as IdentificationChallenge}></ak-stage-identification>`; | ||||
|                     case "ak-stage-password": | ||||
|                         return html`<ak-stage-password .host=${this} .challenge=${this.challenge as PasswordChallenge}></ak-stage-password>`; | ||||
|                     case "ak-stage-captcha": | ||||
|                         return html`<ak-stage-captcha .host=${this} .challenge=${this.challenge as CaptchaChallenge}></ak-stage-captcha>`; | ||||
|                     case "ak-stage-consent": | ||||
|                         return html`<ak-stage-consent .host=${this} .challenge=${this.challenge as ConsentChallenge}></ak-stage-consent>`; | ||||
|                     case "ak-stage-email": | ||||
|                         return html`<ak-stage-email .host=${this} .challenge=${this.challenge as EmailChallenge}></ak-stage-email>`; | ||||
|                     case "ak-stage-autosubmit": | ||||
|                         return html`<ak-stage-autosubmit .host=${this} .challenge=${this.challenge as AutosubmitChallenge}></ak-stage-autosubmit>`; | ||||
|                     case "ak-stage-prompt": | ||||
|                         return html`<ak-stage-prompt .host=${this} .challenge=${this.challenge as PromptChallenge}></ak-stage-prompt>`; | ||||
|                     case "ak-stage-authenticator-totp": | ||||
|                         return html`<ak-stage-authenticator-totp .host=${this} .challenge=${this.challenge as AuthenticatorTOTPChallenge}></ak-stage-authenticator-totp>`; | ||||
|                     case "ak-stage-authenticator-static": | ||||
|                         return html`<ak-stage-authenticator-static .host=${this} .challenge=${this.challenge as AuthenticatorStaticChallenge}></ak-stage-authenticator-static>`; | ||||
|                     case "ak-stage-authenticator-webauthn": | ||||
|                         return html`<ak-stage-authenticator-webauthn .host=${this} .challenge=${this.challenge as WebAuthnAuthenticatorRegisterChallenge}></ak-stage-authenticator-webauthn>`; | ||||
|                     case "ak-stage-authenticator-validate": | ||||
|                         return html`<ak-stage-authenticator-validate .host=${this} .challenge=${this.challenge as AuthenticatorValidateStageChallenge}></ak-stage-authenticator-validate>`; | ||||
|                     default: | ||||
|                         break; | ||||
|                 } | ||||
|                 break; | ||||
|             default: | ||||
|                 console.debug(`authentik/flows: unexpected data type ${this.challenge.type}`); | ||||
|                 break; | ||||
|         } | ||||
|         return html``; | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         if (!this.challenge) { | ||||
|             return this.renderLoading(); | ||||
|         } | ||||
|         return html` | ||||
|             ${this.loading ? this.renderLoading() : html``} | ||||
|             ${this.renderChallenge()} | ||||
|         `; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L