stages: source stage (#8330)

* stages: source stage

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* include stage name in dummy stage

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use data instead of instance for login button

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make mostly work

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix ident stage

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make it work

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* pass more data

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix flow inspector not always loading

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix dark theme for stepper

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix inspector styling

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* don't skip source stage unless returning

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* auto open flow inspector when debug

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix lint

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fixup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix lint

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix validation

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* include raw saml response in flow context

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add some tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* move

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add docs

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Jens L. <jens@beryju.org>

* fix import

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
This commit is contained in:
Jens L
2024-03-14 19:46:27 +01:00
committed by GitHub
parent 5805ac83f7
commit fdcc1dcb36
34 changed files with 1107 additions and 21 deletions

View File

@ -15,6 +15,7 @@ import "@goauthentik/admin/stages/identification/IdentificationStageForm";
import "@goauthentik/admin/stages/invitation/InvitationStageForm";
import "@goauthentik/admin/stages/password/PasswordStageForm";
import "@goauthentik/admin/stages/prompt/PromptStageForm";
import "@goauthentik/admin/stages/source/SourceStageForm";
import "@goauthentik/admin/stages/user_delete/UserDeleteStageForm";
import "@goauthentik/admin/stages/user_login/UserLoginStageForm";
import "@goauthentik/admin/stages/user_logout/UserLogoutStageForm";

View File

@ -1,3 +1,4 @@
import "@goauthentik/admin/common/ak-license-notice";
import { StageBindingForm } from "@goauthentik/admin/flows/StageBindingForm";
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
@ -14,12 +15,14 @@ import "@goauthentik/admin/stages/identification/IdentificationStageForm";
import "@goauthentik/admin/stages/invitation/InvitationStageForm";
import "@goauthentik/admin/stages/password/PasswordStageForm";
import "@goauthentik/admin/stages/prompt/PromptStageForm";
import "@goauthentik/admin/stages/source/SourceStageForm";
import "@goauthentik/admin/stages/user_delete/UserDeleteStageForm";
import "@goauthentik/admin/stages/user_login/UserLoginStageForm";
import "@goauthentik/admin/stages/user_logout/UserLogoutStageForm";
import "@goauthentik/admin/stages/user_write/UserWriteStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { AKElement } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import "@goauthentik/elements/forms/ProxyForm";
import "@goauthentik/elements/wizard/FormWizardPage";
import { FormWizardPage } from "@goauthentik/elements/wizard/FormWizardPage";
@ -28,7 +31,7 @@ import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";
import { msg, str } from "@lit/localize";
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
import { CSSResult, TemplateResult, html } from "lit";
import { CSSResult, TemplateResult, html, nothing } from "lit";
import { property } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
@ -39,7 +42,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { FlowStageBinding, Stage, StagesApi, TypeCreate } from "@goauthentik/api";
@customElement("ak-stage-wizard-initial")
export class InitialStageWizardPage extends WizardPage {
export class InitialStageWizardPage extends WithLicenseSummary(WizardPage) {
@property({ attribute: false })
stageTypes: TypeCreate[] = [];
sidebarLabel = () => msg("Select type");
@ -62,6 +65,7 @@ export class InitialStageWizardPage extends WizardPage {
render(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal">
${this.stageTypes.map((type) => {
const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
return html`<div class="pf-c-radio">
<input
class="pf-c-radio__input"
@ -82,11 +86,15 @@ export class InitialStageWizardPage extends WizardPage {
);
this.host.isValid = true;
}}
?disabled=${requiresEnterprise}
/>
<label class="pf-c-radio__label" for=${`${type.component}-${type.modelName}`}
>${type.name}</label
>
<span class="pf-c-radio__description">${type.description}</span>
<span class="pf-c-radio__description">${type.description}${
requiresEnterprise ? html`<ak-license-notice></ak-license-notice>` : nothing
}</span>
</span>
</div>`;
})}
</form>`;

View File

@ -0,0 +1,99 @@
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/SearchSelect/index";
import "@goauthentik/elements/utils/TimeDeltaHelp";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import {
Source,
SourceStage,
SourcesAllListRequest,
SourcesApi,
StagesApi,
} from "@goauthentik/api";
@customElement("ak-stage-source-form")
export class SourceStageForm extends BaseStageForm<SourceStage> {
loadInstance(pk: string): Promise<SourceStage> {
return new StagesApi(DEFAULT_CONFIG).stagesSourceRetrieve({
stageUuid: pk,
});
}
async send(data: SourceStage): Promise<SourceStage> {
if (this.instance) {
return new StagesApi(DEFAULT_CONFIG).stagesSourceUpdate({
stageUuid: this.instance.pk || "",
sourceStageRequest: data,
});
} else {
return new StagesApi(DEFAULT_CONFIG).stagesSourceCreate({
sourceStageRequest: data,
});
}
}
renderForm(): TemplateResult {
return html`
<span> ${msg("TODO.")} </span>
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
<input
type="text"
value="${ifDefined(this.instance?.name || "")}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Source")} ?required=${true} name="source">
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Source[]> => {
const args: SourcesAllListRequest = {
ordering: "name",
};
if (query !== undefined) {
args.search = query;
}
const users = await new SourcesApi(DEFAULT_CONFIG).sourcesAllList(args);
return users.results;
}}
.renderElement=${(source: Source): string => {
return source.name;
}}
.renderDescription=${(source: Source): TemplateResult => {
return html`${source.verboseName}`;
}}
.value=${(source: Source | undefined): string | undefined => {
return source?.pk;
}}
.selected=${(source: Source): boolean => {
return source.pk === this.instance?.source;
}}
>
</ak-search-select>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Resume timeout")}
?required=${true}
name="resumeTimeout"
>
<input
type="text"
value="${ifDefined(this.instance?.resumeTimeout || "minutes=10")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg(
"Amount of time a user can take to return from the source to continue the flow.",
)}
</p>
<ak-utils-time-delta-help></ak-utils-time-delta-help>
</ak-form-element-horizontal>
`;
}
}

View File

@ -329,3 +329,7 @@ input[type="date"]::-webkit-calendar-picker-indicator {
.pf-c-tree-view__content:focus-within {
--pf-c-tree-view__node--hover--BackgroundColor: var(--ak-dark-background-light-ish);
}
/* stepper */
.pf-c-progress-stepper__step-title {
--pf-c-progress-stepper__step-title--Color: var(--ak-dark-foreground);
}

View File

@ -32,6 +32,7 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import {
CapabilitiesEnum,
ChallengeChoices,
ChallengeTypes,
ContextualFlowInfo,
@ -162,7 +163,7 @@ export class FlowExecutor extends Interface implements StageHost {
super();
this.ws = new WebsocketClient();
if (window.location.search.includes("inspector")) {
this.inspectorOpen = !this.inspectorOpen;
this.inspectorOpen = true;
}
this.addEventListener(EVENT_FLOW_INSPECTOR_TOGGLE, () => {
this.inspectorOpen = !this.inspectorOpen;
@ -213,6 +214,9 @@ export class FlowExecutor extends Interface implements StageHost {
async firstUpdated(): Promise<void> {
configureSentry();
if (this.config?.capabilities.includes(CapabilitiesEnum.CanDebug)) {
this.inspectorOpen = true;
}
this.loading = true;
try {
const challenge = await new FlowsApi(DEFAULT_CONFIG).flowsExecutorGet({

View File

@ -37,6 +37,10 @@ export class FlowInspector extends AKElement {
PFDescriptionList,
PFProgressStepper,
css`
.pf-c-drawer__body {
min-height: 100vh;
max-height: 100vh;
}
code.break {
word-break: break-all;
}
@ -45,9 +49,6 @@ export class FlowInspector extends AKElement {
overflow-x: hidden;
white-space: break-spaces;
}
.pf-c-notification-drawer__body {
overflow-x: hidden;
}
`,
];
}
@ -113,6 +114,7 @@ export class FlowInspector extends AKElement {
return this.renderAccessDenied();
}
if (!this.state) {
this.advanceHandler();
return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
</ak-empty-state>`;
}

View File

@ -2,7 +2,7 @@ import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/FormStatic";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement } from "lit/decorators.js";
@ -36,6 +36,7 @@ export class DummyStage extends BaseStage<DummyChallenge, DummyChallengeResponse
this.submitForm(e);
}}
>
<p>${msg(str`Stage name: ${this.challenge.name}`)}</p>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}