web/flow: render prompt inputs without unsafeHTML (#5404)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L 2023-04-28 22:46:34 +03:00 committed by GitHub
parent af7cc8d42d
commit a8332eced6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 72 deletions

View File

@ -50,147 +50,151 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
]; ];
} }
renderPromptInner(prompt: StagePrompt): string { renderPromptInner(prompt: StagePrompt): TemplateResult {
switch (prompt.type) { switch (prompt.type) {
case PromptTypeEnum.Text: case PromptTypeEnum.Text:
return `<input return html`<input
type="text" type="text"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
autocomplete="off" autocomplete="off"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.TextArea: case PromptTypeEnum.TextArea:
return `<textarea return html`<textarea
type="text" type="text"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
autocomplete="off" autocomplete="off"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}"">`; value="${prompt.initialValue}"
></textarea>`;
case PromptTypeEnum.TextReadOnly: case PromptTypeEnum.TextReadOnly:
return `<input return html`<input
type="text" type="text"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
class="pf-c-form-control" class="pf-c-form-control"
readonly readonly
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.TextAreaReadOnly: case PromptTypeEnum.TextAreaReadOnly:
return `<textarea return html`<textarea
type="text" type="text"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
class="pf-c-form-control" class="pf-c-form-control"
readonly readonly
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
></textarea>`;
case PromptTypeEnum.Username: case PromptTypeEnum.Username:
return `<input return html`<input
type="text" type="text"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
autocomplete="username" autocomplete="username"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.Email: case PromptTypeEnum.Email:
return `<input return html`<input
type="email" type="email"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.Password: case PromptTypeEnum.Password:
return `<input return html`<input
type="password" type="password"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
autocomplete="new-password" autocomplete="new-password"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required}>`; ?required=${prompt.required}
/>`;
case PromptTypeEnum.Number: case PromptTypeEnum.Number:
return `<input return html`<input
type="number" type="number"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.Date: case PromptTypeEnum.Date:
return `<input return html`<input
type="date" type="date"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.DateTime: case PromptTypeEnum.DateTime:
return `<input return html`<input
type="datetime" type="datetime"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.File: case PromptTypeEnum.File:
return `<input return html`<input
type="file" type="file"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
placeholder="${prompt.placeholder}" placeholder="${prompt.placeholder}"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required} ?required=${prompt.required}
value="${prompt.initialValue}">`; value="${prompt.initialValue}"
/>`;
case PromptTypeEnum.Separator: case PromptTypeEnum.Separator:
return `<ak-divider>${prompt.placeholder}</ak-divider>`; return html`<ak-divider>${prompt.placeholder}</ak-divider>`;
case PromptTypeEnum.Hidden: case PromptTypeEnum.Hidden:
return `<input return html`<input
type="hidden" type="hidden"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
value="${prompt.initialValue}" value="${prompt.initialValue}"
class="pf-c-form-control" class="pf-c-form-control"
?required=${prompt.required}>`; ?required=${prompt.required}
/>`;
case PromptTypeEnum.Static: case PromptTypeEnum.Static:
return `<p>${prompt.initialValue}</p>`; return html`<p>${unsafeHTML(prompt.initialValue)}</p>`;
case PromptTypeEnum.Dropdown: case PromptTypeEnum.Dropdown:
return `<select class="pf-c-form-control" name="${prompt.fieldKey}"> return html`<select class="pf-c-form-control" name="${prompt.fieldKey}">
${prompt.choices ${prompt.choices?.map((choice) => {
?.map((choice) => { return html`<option
return `<option
value="${choice}" value="${choice}"
?selected=${prompt.initialValue === choice} ?selected=${prompt.initialValue === choice}
> >
${choice} ${choice}
</option>`; </option>`;
}) })}
.join("")}
</select>`; </select>`;
case PromptTypeEnum.RadioButtonGroup: case PromptTypeEnum.RadioButtonGroup:
return ( return html`${(prompt.choices || []).map((choice) => {
prompt.choices const id = `${prompt.fieldKey}-${choice}`;
?.map((choice) => { return html`<div class="pf-c-check">
return ` <div class="pf-c-check">
<input <input
type="radio" type="radio"
class="pf-c-check__input" class="pf-c-check__input"
id="${prompt.fieldKey}"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
id="${id}"
checked="${prompt.initialValue === choice}" checked="${prompt.initialValue === choice}"
required="${prompt.required}" required="${prompt.required}"
value="${choice}" value="${choice}"
/> />
<label class="pf-c-check__label" for="${ <label class="pf-c-check__label" for=${id}>${choice}</label>
prompt.fieldKey </div> `;
}">${choice}</label> })}`;
</div>
`;
})
.join("") || ""
);
case PromptTypeEnum.AkLocale: case PromptTypeEnum.AkLocale:
return `<select class="pf-c-form-control" name="${prompt.fieldKey}"> return html`<select class="pf-c-form-control" name="${prompt.fieldKey}">
<option value="" ${prompt.initialValue === "" ? "selected" : ""}> <option value="" ${prompt.initialValue === "" ? "selected" : ""}>
${t`Auto-detect (based on your browser)`} ${t`Auto-detect (based on your browser)`}
</option> </option>
@ -202,19 +206,17 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
); );
} }
return true; return true;
}) }).map((locale) => {
.map((locale) => { return html`<option
return `<option
value=${locale.code} value=${locale.code}
${prompt.initialValue === locale.code ? "selected" : ""} ${prompt.initialValue === locale.code ? "selected" : ""}
> >
${locale.code.toUpperCase()} - ${locale.label} ${locale.code.toUpperCase()} - ${locale.label}
</option>`; </option>`;
}) })}
.join("")}
</select>`; </select>`;
default: default:
return `<p>invalid type '${prompt.type}'</p>`; return html`<p>invalid type '${prompt.type}'</p>`;
} }
} }
@ -263,11 +265,10 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
class="pf-c-form__group" class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {})[prompt.fieldKey]} .errors=${(this.challenge?.responseErrors || {})[prompt.fieldKey]}
> >
${unsafeHTML(this.renderPromptInner(prompt))} ${this.renderPromptHelpText(prompt)} ${this.renderPromptInner(prompt)} ${this.renderPromptHelpText(prompt)}
</ak-form-element>`; </ak-form-element>`;
} }
return html` ${unsafeHTML(this.renderPromptInner(prompt))} return html` ${this.renderPromptInner(prompt)} ${this.renderPromptHelpText(prompt)}`;
${this.renderPromptHelpText(prompt)}`;
} }
renderContinue(): TemplateResult { renderContinue(): TemplateResult {

View File

@ -5,17 +5,16 @@ import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit"; import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js"; import { customElement } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { PromptTypeEnum, StagePrompt } from "@goauthentik/api"; import { PromptTypeEnum, StagePrompt } from "@goauthentik/api";
@customElement("ak-user-stage-prompt") @customElement("ak-user-stage-prompt")
export class UserSettingsPromptStage extends PromptStage { export class UserSettingsPromptStage extends PromptStage {
renderPromptInner(prompt: StagePrompt): string { renderPromptInner(prompt: StagePrompt): TemplateResult {
switch (prompt.type) { switch (prompt.type) {
// Checkbox requires slightly different rendering here due to the use of horizontal form elements // Checkbox requires slightly different rendering here due to the use of horizontal form elements
case PromptTypeEnum.Checkbox: case PromptTypeEnum.Checkbox:
return `<input return html`<input
type="checkbox" type="checkbox"
class="pf-c-check__input" class="pf-c-check__input"
name="${prompt.fieldKey}" name="${prompt.fieldKey}"
@ -41,14 +40,11 @@ export class UserSettingsPromptStage extends PromptStage {
return error.string; return error.string;
})} })}
> >
${unsafeHTML(this.renderPromptInner(prompt))} ${this.renderPromptInner(prompt)} ${this.renderPromptHelpText(prompt)}
${this.renderPromptHelpText(prompt)}
</ak-form-element-horizontal> </ak-form-element-horizontal>
`; `;
} }
return html` return html` ${this.renderPromptInner(prompt)} ${this.renderPromptHelpText(prompt)} `;
${unsafeHTML(this.renderPromptInner(prompt))} ${this.renderPromptHelpText(prompt)}
`;
} }
renderContinue(): TemplateResult { renderContinue(): TemplateResult {