Now, it's starting to look like a complete package. The LDAP method is working, but there is a bug:
the radio is sending the wrong value !?!?!?. Track that down, dammit. The search wrappers now resend their events as standard `input` events, and that actually seems to work well; the browser is decorating it with the right target, with the right `name` attribute, and since we have good definitions of the `value` as a string (the real value of any search object is its UUID4), that works quite well. Added search wrappers for CoreGroup and CryptoCertificate (CertificateKeyPairs), and the latter has flags for "use the first one if it's the only one" and "allow the display of keyless certificates." Not sure why `state()` is blocking the transmission of typing information from the typed element to the context handler, but it's a bug in the typechecker, and it's not a problem so far.
This commit is contained in:
parent
bf26e5d11e
commit
b4d3b75434
|
@ -0,0 +1,29 @@
|
||||||
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||||
|
|
||||||
|
import { consume } from "@lit-labs/context";
|
||||||
|
import { state } from "@lit/reactive-element/decorators/state.js";
|
||||||
|
|
||||||
|
import { styles as AwadStyles } from "./ak-application-wizard-application-details.css";
|
||||||
|
|
||||||
|
import type { WizardState } from "./ak-application-wizard-context";
|
||||||
|
import applicationWizardContext from "./ak-application-wizard-context-name";
|
||||||
|
|
||||||
|
export class ApplicationWizardPageBase extends CustomEmitterElement(AKElement) {
|
||||||
|
static get styles() {
|
||||||
|
return AwadStyles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@consume({ context: applicationWizardContext, subscribe: true })
|
||||||
|
@state()
|
||||||
|
private wizard!: WizardState;
|
||||||
|
|
||||||
|
dispatchWizardUpdate(update: Partial<WizardState>) {
|
||||||
|
this.dispatchCustomEvent("ak-wizard-update", {
|
||||||
|
...this.wizard,
|
||||||
|
...update,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApplicationWizardPageBase;
|
|
@ -6,6 +6,7 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||||
|
import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";
|
||||||
import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";
|
import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
|
@ -15,10 +16,14 @@ export const styles = [
|
||||||
PFButton,
|
PFButton,
|
||||||
PFForm,
|
PFForm,
|
||||||
PFAlert,
|
PFAlert,
|
||||||
|
PFRadio,
|
||||||
PFInputGroup,
|
PFInputGroup,
|
||||||
PFFormControl,
|
PFFormControl,
|
||||||
PFSwitch,
|
PFSwitch,
|
||||||
css`
|
css`
|
||||||
|
.pf-c-radio__label {
|
||||||
|
color: #212427;
|
||||||
|
}
|
||||||
select[multiple] {
|
select[multiple] {
|
||||||
height: 15em;
|
height: 15em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,39 +3,22 @@ import { first } from "@goauthentik/common/utils";
|
||||||
import "@goauthentik/components/ak-radio-input";
|
import "@goauthentik/components/ak-radio-input";
|
||||||
import "@goauthentik/components/ak-switch-input";
|
import "@goauthentik/components/ak-switch-input";
|
||||||
import "@goauthentik/components/ak-text-input";
|
import "@goauthentik/components/ak-text-input";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
|
||||||
import "@goauthentik/elements/forms/FormGroup";
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
import "@goauthentik/elements/forms/FormGroup";
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { consume } from "@lit-labs/context";
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
||||||
import { state } from "@lit/reactive-element/decorators/state.js";
|
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import { styles as AwadStyles } from "./ak-application-wizard-application-details.css";
|
import ApplicationWizardPageBase from "./ApplicationWizardPageBase";
|
||||||
|
|
||||||
import type { WizardState } from "./ak-application-wizard-context";
|
|
||||||
import applicationWizardContext from "./ak-application-wizard-context-name";
|
|
||||||
|
|
||||||
@customElement("ak-application-wizard-application-details")
|
@customElement("ak-application-wizard-application-details")
|
||||||
export class ApplicationWizardApplicationDetails extends CustomEmitterElement(AKElement) {
|
export class ApplicationWizardApplicationDetails extends ApplicationWizardPageBase {
|
||||||
static get styles() {
|
|
||||||
return AwadStyles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@consume({ context: applicationWizardContext, subscribe: true })
|
|
||||||
@state()
|
|
||||||
private wizard!: WizardState;
|
|
||||||
|
|
||||||
handleChange(ev: Event) {
|
handleChange(ev: Event) {
|
||||||
const value = ev.target.type === "checkbox" ? ev.target.checked : ev.target.value;
|
const value = ev.target.type === "checkbox" ? ev.target.checked : ev.target.value;
|
||||||
|
this.dispatchWizardUpdate({
|
||||||
this.dispatchCustomEvent("ak-wizard-update", {
|
|
||||||
...this.wizard,
|
|
||||||
application: {
|
application: {
|
||||||
...this.wizard.application,
|
...this.wizard.application,
|
||||||
[ev.target.name]: value,
|
[ev.target.name]: value,
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
|
import "@goauthentik/components/ak-radio-input";
|
||||||
|
import "@goauthentik/components/ak-switch-input";
|
||||||
|
import "@goauthentik/components/ak-text-input";
|
||||||
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
|
|
||||||
|
import { msg } from "@lit/localize";
|
||||||
|
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
||||||
|
import { html } from "lit";
|
||||||
|
import { state } from "lit/decorators.js";
|
||||||
|
import { map } from "lit/directives/map.js";
|
||||||
|
|
||||||
|
import { ProvidersApi } from "@goauthentik/api";
|
||||||
|
import type { TypeCreate } from "@goauthentik/api";
|
||||||
|
|
||||||
|
import ApplicationWizardPageBase from "./ApplicationWizardPageBase";
|
||||||
|
|
||||||
|
@customElement("ak-application-wizard-authentication-method-choice")
|
||||||
|
export class ApplicationWizardAuthenticationMethodChoice extends ApplicationWizardPageBase {
|
||||||
|
@state()
|
||||||
|
providerTypes: TypeCreate[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.handleChoice = this.handleChoice.bind(this);
|
||||||
|
this.renderProvider = this.renderProvider.bind(this);
|
||||||
|
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
|
||||||
|
this.providerTypes = types;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChoice(ev: Event) {
|
||||||
|
this.dispatchWizardUpdate({ providerType: ev.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderProvider(type: Provider) {
|
||||||
|
// Special case; the SAML-by-import method is handled differently
|
||||||
|
// prettier-ignore
|
||||||
|
const model = /^SAML/.test(type.name) && type.modelName === ""
|
||||||
|
? "samlimporter"
|
||||||
|
: type.modelName;
|
||||||
|
|
||||||
|
return html`<div class="pf-c-radio">
|
||||||
|
<input
|
||||||
|
class="pf-c-radio__input"
|
||||||
|
type="radio"
|
||||||
|
name="type"
|
||||||
|
id=${type.component}
|
||||||
|
value=${model}
|
||||||
|
@change=${this.handleChoice}
|
||||||
|
/>
|
||||||
|
<label class="pf-c-radio__label" for=${type.component}>${type.name}</label>
|
||||||
|
<span class="pf-c-radio__description">${type.description}</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return this.providerTypes.length > 0
|
||||||
|
? html`<form class="pf-c-form pf-m-horizontal">
|
||||||
|
${map(this.providerTypes, this.renderProvider)}
|
||||||
|
</form>`
|
||||||
|
: html`<ak-empty-state loading header=${msg("Loading")}></ak-empty-state>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApplicationWizardAuthenticationMethodChoice;
|
|
@ -29,6 +29,7 @@ type OneOfProvider =
|
||||||
|
|
||||||
export type WizardState = {
|
export type WizardState = {
|
||||||
step: number;
|
step: number;
|
||||||
|
providerType: string;
|
||||||
application: Partial<Application>;
|
application: Partial<Application>;
|
||||||
provider: OneOfProvider;
|
provider: OneOfProvider;
|
||||||
};
|
};
|
||||||
|
@ -42,6 +43,7 @@ export class AkApplicationWizardContext extends CustomListenerElement(LitElement
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
wizardState: WizardState = {
|
wizardState: WizardState = {
|
||||||
step: 0,
|
step: 0,
|
||||||
|
providerType: "",
|
||||||
application: {},
|
application: {},
|
||||||
provider: {},
|
provider: {},
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,9 +4,35 @@ import { TemplateResult, html } from "lit";
|
||||||
|
|
||||||
import "../ak-application-wizard-application-details";
|
import "../ak-application-wizard-application-details";
|
||||||
import AkApplicationWizardApplicationDetails from "../ak-application-wizard-application-details";
|
import AkApplicationWizardApplicationDetails from "../ak-application-wizard-application-details";
|
||||||
|
import "../ak-application-wizard-authentication-method-choice";
|
||||||
import "../ak-application-wizard-context";
|
import "../ak-application-wizard-context";
|
||||||
import "./ak-application-context-display-for-test";
|
import "./ak-application-context-display-for-test";
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const providerTypes = [
|
||||||
|
["LDAP Provider", "ldapprovider",
|
||||||
|
"Allow applications to authenticate against authentik's users using LDAP.",
|
||||||
|
],
|
||||||
|
["OAuth2/OpenID Provider", "oauth2provider",
|
||||||
|
"OAuth2 Provider for generic OAuth and OpenID Connect Applications.",
|
||||||
|
],
|
||||||
|
["Proxy Provider", "proxyprovider",
|
||||||
|
"Protect applications that don't support any of the other\n Protocols by using a Reverse-Proxy.",
|
||||||
|
],
|
||||||
|
["Radius Provider", "radiusprovider",
|
||||||
|
"Allow applications to authenticate against authentik's users using Radius.",
|
||||||
|
],
|
||||||
|
["SAML Provider", "samlprovider",
|
||||||
|
"SAML 2.0 Endpoint for applications which support SAML.",
|
||||||
|
],
|
||||||
|
["SCIM Provider", "scimprovider",
|
||||||
|
"SCIM 2.0 provider to create users and groups in external applications",
|
||||||
|
],
|
||||||
|
["SAML Provider from Metadata", "",
|
||||||
|
"Create a SAML Provider by importing its Metadata.",
|
||||||
|
],
|
||||||
|
].map(([name, model_name, description]) => ({ name, description, model_name }));
|
||||||
|
|
||||||
const metadata: Meta<AkApplicationWizardApplicationDetails> = {
|
const metadata: Meta<AkApplicationWizardApplicationDetails> = {
|
||||||
title: "Elements / Application Wizard / Page 1",
|
title: "Elements / Application Wizard / Page 1",
|
||||||
component: "ak-application-wizard-application-details",
|
component: "ak-application-wizard-application-details",
|
||||||
|
@ -16,6 +42,14 @@ const metadata: Meta<AkApplicationWizardApplicationDetails> = {
|
||||||
component: "The first page of the application wizard",
|
component: "The first page of the application wizard",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mockData: [
|
||||||
|
{
|
||||||
|
url: "/api/v3/providers/all/types/",
|
||||||
|
method: "GET",
|
||||||
|
status: 200,
|
||||||
|
response: providerTypes,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,3 +76,12 @@ export const PageOne = () => {
|
||||||
</ak-application-wizard-context>`
|
</ak-application-wizard-context>`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PageTwo = () => {
|
||||||
|
return container(
|
||||||
|
html`<ak-application-wizard-context>
|
||||||
|
<ak-application-wizard-authentication-method-choice></ak-application-wizard-authentication-method-choice>
|
||||||
|
<ak-application-context-display-for-test></ak-application-context-display-for-test>
|
||||||
|
</ak-application-wizard-context>`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
Reference in New Issue