This commit continues the application wizard buildout. In this commit are the following changes:
- Added SCIM to the list of available providers - Fixed ForwardProxy so that its mode is set correctly. (This is a special case in the committer; I'm unhappy with that.) - Fixed the commit messages so that: - icons are set correctly (Success, Danger, Working) - icons are colored correctly according to state - commit message includes a `data-commit-state` field so tests can find it! - Merged the application wizard tests into a single test pass - Isolated common parts of the application wizard tests to reduce unnecessary repetition. All application tests are the same until you reach the provider section anyway. - Fixed the unit tests so they're finding the right error messages and are enabled to display them correctly. - Moved the test Form handlers into their own folder so they're not cluttering up the Pages folder.
This commit is contained in:
parent
7e6a9fd3d2
commit
f2ba927d34
|
@ -37,5 +37,5 @@ test-bad-login: node_modules admin-user ## Test that bad usernames and password
|
|||
$(SPEC)/bad-logins.ts
|
||||
|
||||
|
||||
test-application-wizard-ldap: node_modules admin-user
|
||||
$(SPEC)/new-wizard-ldap-application.ts
|
||||
test-application-wizard:
|
||||
$(SPEC)/new-application-by-wizard.ts
|
||||
|
|
|
@ -4,8 +4,9 @@ import Page from "../pageobjects/page.js";
|
|||
const CLICK_TIME_DELAY = 250;
|
||||
|
||||
export default class AdminPage extends Page {
|
||||
|
||||
public get pageHeader() {
|
||||
return $(">>>ak-page-header h1");
|
||||
return $('>>>ak-page-header slot[name="header"]');
|
||||
}
|
||||
|
||||
async openApplicationsListPage() {
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
import { $ } from "@wdio/globals";
|
||||
import AdminPage from "./admin.page.js";
|
||||
import ApplicationForm from "./application-form.view.js";
|
||||
import LdapForm from "./ldap-form.view.js";
|
||||
import OauthForm from "./oauth-form.view.js";
|
||||
import ApplicationForm from "./forms/application.form.js";
|
||||
import LdapForm from "./forms/ldap.form.js";
|
||||
import OauthForm from "./forms/oauth.form.js";
|
||||
import TransparentProxyForm from "./forms/transparent-proxy.form.js";
|
||||
import ForwardProxyForm from "./forms/forward-proxy.form.js";
|
||||
import SamlForm from "./forms/saml.form.js";
|
||||
import ScimForm from "./forms/scim.form.js";
|
||||
import { $ } from "@wdio/globals";
|
||||
|
||||
/**
|
||||
* sub page containing specific selectors and methods for a specific page
|
||||
*/
|
||||
|
||||
class ApplicationWizardView extends AdminPage {
|
||||
/**
|
||||
* define selectors using getter methods
|
||||
|
@ -14,6 +19,10 @@ class ApplicationWizardView extends AdminPage {
|
|||
|
||||
ldap = LdapForm;
|
||||
oauth = OauthForm;
|
||||
transparentProxy = TransparentProxyForm;
|
||||
forwardProxy = ForwardProxyForm;
|
||||
saml = SamlForm;
|
||||
scim = ScimForm;
|
||||
app = ApplicationForm;
|
||||
|
||||
get wizardTitle() {
|
||||
|
@ -32,9 +41,33 @@ class ApplicationWizardView extends AdminPage {
|
|||
return await this.providerList.$(`>>>input[value="${type}"]`);
|
||||
}
|
||||
|
||||
get commitMessage() {
|
||||
return $(">>>ak-application-wizard-commit-application h1.pf-c-title");
|
||||
get successMessage() {
|
||||
return $('>>>[data-commit-state="success"]')
|
||||
}
|
||||
}
|
||||
|
||||
type Pair = [string, string];
|
||||
|
||||
// Define a getter for each provider type in the radio button collection.
|
||||
|
||||
const providerValues: Pair[] = [
|
||||
["oauth2provider", "oauth2Provider"],
|
||||
["ldapprovider", "ldapProvider"],
|
||||
["proxyprovider-proxy", "proxyProviderProxy"],
|
||||
["proxyprovider-forwardsingle", "proxyProviderForwardsingle"],
|
||||
["radiusprovider", "radiusProvider"],
|
||||
["samlprovider", "samlProvider"],
|
||||
["scimprovider", "scimProvider"],
|
||||
];
|
||||
|
||||
providerValues.forEach(([value, name]: Pair) => {
|
||||
Object.defineProperties(ApplicationWizardView.prototype, {
|
||||
[name]: {
|
||||
get: function () {
|
||||
return this.providerList.$(`>>>input[value="${value}"]`);
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
export default new ApplicationWizardView();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { $ } from "@wdio/globals";
|
||||
import Page from "./page.js";
|
||||
import Page from "../page.js";
|
||||
|
||||
export class ApplicationForm extends Page {
|
||||
get name() {
|
18
tests/wdio/test/pageobjects/forms/forward-proxy.form.ts
Normal file
18
tests/wdio/test/pageobjects/forms/forward-proxy.form.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { $ } from "@wdio/globals";
|
||||
import Page from "../page.js";
|
||||
|
||||
export class ForwardProxyForm extends Page {
|
||||
async setAuthorizationFlow(selector: string) {
|
||||
await this.searchSelect(
|
||||
'>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
|
||||
"authorizationFlow",
|
||||
`button*=${selector}`
|
||||
);
|
||||
}
|
||||
|
||||
get externalHost() {
|
||||
return $('>>>input[name="externalHost"]');
|
||||
}
|
||||
}
|
||||
|
||||
export default new ForwardProxyForm();
|
|
@ -1,4 +1,4 @@
|
|||
import Page from "./page.js";
|
||||
import Page from "../page.js";
|
||||
|
||||
export class LdapForm extends Page {
|
||||
async setBindFlow(selector: string) {
|
|
@ -1,4 +1,4 @@
|
|||
import Page from "./page.js";
|
||||
import Page from "../page.js";
|
||||
|
||||
export class OauthForm extends Page {
|
||||
async setAuthorizationFlow(selector: string) {
|
18
tests/wdio/test/pageobjects/forms/saml.form.ts
Normal file
18
tests/wdio/test/pageobjects/forms/saml.form.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { $ } from "@wdio/globals";
|
||||
import Page from "../page.js";
|
||||
|
||||
export class SamlForm extends Page {
|
||||
async setAuthorizationFlow(selector: string) {
|
||||
await this.searchSelect(
|
||||
'>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
|
||||
"authorizationFlow",
|
||||
`button*=${selector}`
|
||||
);
|
||||
}
|
||||
|
||||
get acsUrl() {
|
||||
return $('>>>input[name="acsUrl"]');
|
||||
}
|
||||
}
|
||||
|
||||
export default new SamlForm();
|
14
tests/wdio/test/pageobjects/forms/scim.form.ts
Normal file
14
tests/wdio/test/pageobjects/forms/scim.form.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Page from "../page.js";
|
||||
|
||||
export class ScimForm extends Page {
|
||||
|
||||
get url() {
|
||||
return $('>>>input[name="url"]');
|
||||
}
|
||||
|
||||
get token() {
|
||||
return $('>>>input[name="token"]');
|
||||
}
|
||||
}
|
||||
|
||||
export default new ScimForm();
|
22
tests/wdio/test/pageobjects/forms/transparent-proxy.form.ts
Normal file
22
tests/wdio/test/pageobjects/forms/transparent-proxy.form.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { $ } from "@wdio/globals";
|
||||
import Page from "../page.js";
|
||||
|
||||
export class TransparentProxyForm extends Page {
|
||||
async setAuthorizationFlow(selector: string) {
|
||||
await this.searchSelect(
|
||||
'>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
|
||||
"authorizationFlow",
|
||||
`button*=${selector}`
|
||||
);
|
||||
}
|
||||
|
||||
get externalHost() {
|
||||
return $('>>>input[name="externalHost"]');
|
||||
}
|
||||
|
||||
get internalHost() {
|
||||
return $('>>>input[name="internalHost"]');
|
||||
}
|
||||
}
|
||||
|
||||
export default new TransparentProxyForm();
|
|
@ -38,4 +38,9 @@ export default class Page {
|
|||
const target = searchBlock.$(buttonSelector);
|
||||
return await target.click();
|
||||
}
|
||||
|
||||
public async logout() {
|
||||
await browser.url('http://localhost:9000/flows/-/default/invalidation/');
|
||||
return await this.pause()
|
||||
}
|
||||
}
|
||||
|
|
148
tests/wdio/test/specs/new-application-by-wizard.ts
Normal file
148
tests/wdio/test/specs/new-application-by-wizard.ts
Normal file
|
@ -0,0 +1,148 @@
|
|||
import ApplicationWizardView from "../pageobjects/application-wizard.page.js";
|
||||
import ApplicationsListPage from "../pageobjects/applications-list.page.js";
|
||||
import { randomId } from "../utils/index.js";
|
||||
import { login } from "../utils/login.js";
|
||||
import { expect } from "@wdio/globals";
|
||||
|
||||
async function reachTheProvider(title: string) {
|
||||
const newPrefix = randomId();
|
||||
|
||||
await ApplicationsListPage.logout();
|
||||
await login();
|
||||
await ApplicationsListPage.open();
|
||||
await expect(await ApplicationsListPage.pageHeader).toHaveText("Applications");
|
||||
|
||||
await ApplicationsListPage.startWizardButton.click();
|
||||
await ApplicationWizardView.wizardTitle.waitForDisplayed();
|
||||
await expect(await ApplicationWizardView.wizardTitle).toHaveText("New application");
|
||||
|
||||
await ApplicationWizardView.app.name.setValue(`${title} - ${newPrefix}`);
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
return await ApplicationWizardView.pause();
|
||||
}
|
||||
|
||||
|
||||
async function getCommitMessage() {
|
||||
await ApplicationWizardView.successMessage.waitForDisplayed();
|
||||
return await ApplicationWizardView.successMessage;
|
||||
}
|
||||
|
||||
|
||||
describe("Configure Applications with the Application Wizard", () => {
|
||||
it("Should configure a simple LDAP Application", async () => {
|
||||
await reachTheProvider("New LDAP Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.ldapProvider.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await ApplicationWizardView.ldap.setBindFlow("default-authentication-flow");
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await expect(getCommitMessage()).toHaveText(
|
||||
"Your application has been saved"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should configure a simple Oauth2 Application", async () => {
|
||||
await reachTheProvider("New Oauth2 Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.oauth2Provider.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await ApplicationWizardView.oauth.setAuthorizationFlow(
|
||||
"default-provider-authorization-explicit-consent"
|
||||
);
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await expect(getCommitMessage()).toHaveText(
|
||||
"Your application has been saved"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should configure a simple SAML Application", async () => {
|
||||
await reachTheProvider("New SAML Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.samlProvider.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await ApplicationWizardView.saml.setAuthorizationFlow(
|
||||
"default-provider-authorization-explicit-consent"
|
||||
);
|
||||
await ApplicationWizardView.saml.acsUrl.setValue("http://example.com:8000/");
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await expect(getCommitMessage()).toHaveText(
|
||||
"Your application has been saved"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should configure a simple SCIM Application", async () => {
|
||||
await reachTheProvider("New SCIM Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.scimProvider.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await ApplicationWizardView.scim.url.setValue("http://example.com:8000/");
|
||||
await ApplicationWizardView.scim.token.setValue("a-very-basic-token");
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await expect(getCommitMessage()).toHaveText(
|
||||
"Your application has been saved"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should configure a simple Transparent Proxy Application", async () => {
|
||||
await reachTheProvider("New Transparent Proxy Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.proxyProviderProxy.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await ApplicationWizardView.transparentProxy.setAuthorizationFlow(
|
||||
"default-provider-authorization-explicit-consent"
|
||||
);
|
||||
await ApplicationWizardView.transparentProxy.externalHost.setValue("http://external.example.com");
|
||||
await ApplicationWizardView.transparentProxy.internalHost.setValue("http://internal.example.com");
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await expect(getCommitMessage()).toHaveText(
|
||||
"Your application has been saved"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should configure a simple Forward Proxy Application", async () => {
|
||||
await reachTheProvider("New Forward Proxy Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.proxyProviderForwardsingle.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await ApplicationWizardView.forwardProxy.setAuthorizationFlow(
|
||||
"default-provider-authorization-explicit-consent"
|
||||
);
|
||||
await ApplicationWizardView.forwardProxy.externalHost.setValue("http://external.example.com");
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await expect(getCommitMessage()).toHaveText(
|
||||
"Your application has been saved"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,35 +0,0 @@
|
|||
import { login } from "../utils/login.js";
|
||||
import { randomId } from "../utils/index.js";
|
||||
import ApplicationsListPage from "../pageobjects/applications-list.page.js";
|
||||
import ApplicationWizardView from "../pageobjects/application-wizard.page.js";
|
||||
import { expect } from "@wdio/globals";
|
||||
|
||||
describe("Configure LDAP Application with Wizard", () => {
|
||||
it("Should configure a simple LDAP Application", async () => {
|
||||
const newPrefix = randomId();
|
||||
|
||||
await login();
|
||||
await ApplicationsListPage.open();
|
||||
expect(await ApplicationsListPage.pageHeader).toHaveText("Applications");
|
||||
|
||||
await ApplicationsListPage.startWizardButton.click();
|
||||
await ApplicationWizardView.wizardTitle.waitForDisplayed();
|
||||
expect(await ApplicationWizardView.wizardTitle).toHaveText("New Application");
|
||||
|
||||
await ApplicationWizardView.app.name.setValue(`New LDAP Application - ${newPrefix}`);
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause()
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.providerList.$('>>>input[value=ldapprovider]').click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause()
|
||||
|
||||
await ApplicationWizardView.ldap.setBindFlow('default-authentication-flow');
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause()
|
||||
|
||||
await ApplicationWizardView.commitMessage.waitForDisplayed();
|
||||
expect(await ApplicationWizardView.commitMessage).toHaveText("Your application has been saved");
|
||||
})
|
||||
});
|
|
@ -15,7 +15,7 @@
|
|||
"build-proxy": "run-s build-locales rollup:build-proxy",
|
||||
"watch": "run-s build-locales rollup:watch",
|
||||
"lint": "eslint . --max-warnings 0 --fix",
|
||||
"lint:precommit": "eslint --max-warnings 0 --config ./.eslintrc.precommit.json $(git status --porcelain | grep '^[M?][M?]' | cut -c8- | grep -E '\\.(ts|js|tsx|jsx)$') ",
|
||||
"lint:precommit": "eslint --max-warnings 0 --config ./.eslintrc.precommit.json $(git status --porcelain . | grep '^[M?][M?]' | cut -c8- | grep -E '\\.(ts|js|tsx|jsx)$') ",
|
||||
"lint:spelling": "codespell -D - -D ../.github/codespell-dictionary.txt -I ../.github/codespell-words.txt -S './src/locales/**' ./src -s",
|
||||
"lit-analyse": "lit-analyzer src",
|
||||
"precommit": "run-s tsc lit-analyse lint:precommit lint:spelling prettier",
|
||||
|
|
|
@ -9,6 +9,7 @@ import type {
|
|||
OAuth2ProviderRequest,
|
||||
ProxyProviderRequest,
|
||||
SAMLProviderRequest,
|
||||
SCIMProviderRequest,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { OneOfProvider } from "../types";
|
||||
|
@ -57,18 +58,18 @@ const _providerModelsTable: ProviderType[] = [
|
|||
|
||||
],
|
||||
[
|
||||
"samlprovider-manual",
|
||||
"samlprovider",
|
||||
msg("SAML Manual configuration"),
|
||||
msg("Configure SAML provider manually"),
|
||||
() => html`<ak-application-wizard-authentication-by-saml-configuration></ak-application-wizard-authentication-by-saml-configuration>`,
|
||||
ProviderModelEnum.SamlSamlprovider
|
||||
],
|
||||
[
|
||||
"samlprovider-import",
|
||||
msg("SAML Import Configuration"),
|
||||
msg("Create a SAML provider by importing its metadata"),
|
||||
() => html`<ak-application-wizard-authentication-by-saml-import></ak-application-wizard-authentication-by-saml-import>`,
|
||||
ProviderModelEnum.SamlSamlprovider
|
||||
"scimprovider",
|
||||
msg("SCIM Manual configuration"),
|
||||
msg("Configure SCIM provider manually"),
|
||||
() => html`<ak-application-wizard-authentication-by-scim></ak-application-wizard-authentication-by-scim>`,
|
||||
ProviderModelEnum.ScimScimprovider
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -101,6 +102,13 @@ const converters = new Map<ProviderModelEnumType, ModelConverter>([
|
|||
...(provider as SAMLProviderRequest),
|
||||
}),
|
||||
],
|
||||
[
|
||||
ProviderModelEnum.ScimScimprovider,
|
||||
(provider: OneOfProvider) => ({
|
||||
providerModel: ProviderModelEnum.ScimScimprovider,
|
||||
...(provider as SCIMProviderRequest),
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
||||
// Contract enforcement
|
||||
|
|
|
@ -10,6 +10,7 @@ import "@goauthentik/elements/forms/HorizontalFormElement";
|
|||
import { msg } from "@lit/localize";
|
||||
import { customElement, state } from "@lit/reactive-element/decorators.js";
|
||||
import { TemplateResult, css, html, nothing } from "lit";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";
|
||||
|
@ -19,6 +20,7 @@ import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
|
|||
import {
|
||||
ApplicationRequest,
|
||||
CoreApi,
|
||||
ProxyMode,
|
||||
TransactionApplicationRequest,
|
||||
TransactionApplicationResponse,
|
||||
} from "@goauthentik/api";
|
||||
|
@ -37,15 +39,34 @@ function cleanApplication(app: Partial<ApplicationRequest>): ApplicationRequest
|
|||
|
||||
type ProviderModelType = Exclude<ModelRequest["providerModel"], "11184809">;
|
||||
|
||||
type State = { state: "idle" | "running" | "error" | "done"; label: string | TemplateResult };
|
||||
type State = {
|
||||
state: "idle" | "running" | "error" | "success";
|
||||
label: string | TemplateResult;
|
||||
icon: string[];
|
||||
};
|
||||
|
||||
const idleState: State = { state: "idle", label: "" };
|
||||
const runningState: State = { state: "running", label: msg("Saving Application...") };
|
||||
const idleState: State = {
|
||||
state: "idle",
|
||||
label: "",
|
||||
icon: ["fa-cogs", "pf-m-pending"],
|
||||
};
|
||||
|
||||
const runningState: State = {
|
||||
state: "running",
|
||||
label: msg("Saving Application..."),
|
||||
icon: ["fa-cogs", "pf-m-info"],
|
||||
};
|
||||
const errorState: State = {
|
||||
state: "error",
|
||||
label: msg("There was an error in saving your application:"),
|
||||
icon: ["fa-times-circle", "pf-m-danger"],
|
||||
};
|
||||
|
||||
const successState: State = {
|
||||
state: "success",
|
||||
label: msg("Your application has been saved"),
|
||||
icon: ["fa-check-circle", "pf-m-success"],
|
||||
};
|
||||
const doneState: State = { state: "done", label: msg("Your application has been saved") };
|
||||
|
||||
function extract(o: Record<string, any>): string[] {
|
||||
function inner(o: Record<string, any>): string[] {
|
||||
|
@ -92,10 +113,10 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
|||
if (this.commitState === idleState) {
|
||||
this.response = undefined;
|
||||
this.commitState = runningState;
|
||||
const provider = providerModelsList.find(
|
||||
const providerModel = providerModelsList.find(
|
||||
({ formName }) => formName === this.wizard.providerModel,
|
||||
);
|
||||
if (!provider) {
|
||||
if (!providerModel) {
|
||||
throw new Error(
|
||||
`Could not determine provider model from user request: ${JSON.stringify(
|
||||
this.wizard,
|
||||
|
@ -105,10 +126,26 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
|||
);
|
||||
}
|
||||
|
||||
const provider = (() => {
|
||||
if (this.wizard.providerModel === "proxyprovider-forwardsingle") {
|
||||
return {
|
||||
...providerModel.converter(this.wizard.provider),
|
||||
mode: ProxyMode.ForwardSingle,
|
||||
};
|
||||
}
|
||||
if (this.wizard.providerModel === "proxyprovider-proxy") {
|
||||
return {
|
||||
...providerModel.converter(this.wizard.provider),
|
||||
mode: ProxyMode.Proxy,
|
||||
};
|
||||
}
|
||||
return providerModel.converter(this.wizard.provider);
|
||||
})();
|
||||
|
||||
const request: TransactionApplicationRequest = {
|
||||
providerModel: provider.modelName as ProviderModelType,
|
||||
providerModel: providerModel.modelName as ProviderModelType,
|
||||
app: cleanApplication(this.wizard.app),
|
||||
provider: provider.converter(this.wizard.provider),
|
||||
provider,
|
||||
};
|
||||
|
||||
this.send(request);
|
||||
|
@ -129,7 +166,7 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
|||
this.response = response;
|
||||
this.dispatchCustomEvent(EVENT_REFRESH);
|
||||
this.dispatchWizardUpdate({ status: "submitted" });
|
||||
this.commitState = doneState;
|
||||
this.commitState = successState;
|
||||
})
|
||||
.catch((resolution: any) => {
|
||||
resolution.response.json().then((body: Record<string, any>) => {
|
||||
|
@ -140,16 +177,23 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
|||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
const icon = classMap(this.commitState.icon.reduce((acc, icon) => ({ ...acc, [icon]: true }), {}));
|
||||
|
||||
return html`
|
||||
<div>
|
||||
<div class="pf-l-bullseye">
|
||||
<div class="pf-c-empty-state pf-m-lg">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i
|
||||
class="fas fa- fa-cogs pf-c-empty-state__icon"
|
||||
class="fas fa- ${icon} pf-c-empty-state__icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<h1 class="pf-c-title pf-m-lg">${this.commitState.label}</h1>
|
||||
<h1
|
||||
data-commit-state=${this.commitState.state}
|
||||
class="pf-c-title pf-m-lg"
|
||||
>
|
||||
${this.commitState.label}
|
||||
</h1>
|
||||
${this.errors.length > 0
|
||||
? html`<ul>
|
||||
${this.errors.map(
|
||||
|
|
|
@ -7,7 +7,7 @@ import "./oauth/ak-application-wizard-authentication-by-oauth";
|
|||
import "./proxy/ak-application-wizard-authentication-for-reverse-proxy";
|
||||
import "./proxy/ak-application-wizard-authentication-for-single-forward-proxy";
|
||||
import "./saml/ak-application-wizard-authentication-by-saml-configuration";
|
||||
import "./saml/ak-application-wizard-authentication-by-saml-import";
|
||||
import "./scim/ak-application-wizard-authentication-by-scim";
|
||||
|
||||
// prettier-ignore
|
||||
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement, state } from "@lit/reactive-element/decorators.js";
|
||||
import { html } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
CoreApi,
|
||||
CoreGroupsListRequest,
|
||||
type Group,
|
||||
PaginatedSCIMMappingList,
|
||||
PropertymappingsApi,
|
||||
type SCIMProvider,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import BaseProviderPanel from "../BaseProviderPanel";
|
||||
|
||||
@customElement("ak-application-wizard-authentication-by-scim")
|
||||
export class ApplicationWizardAuthenticationBySCIM extends BaseProviderPanel {
|
||||
@state()
|
||||
propertyMappings?: PaginatedSCIMMappingList;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||
.propertymappingsScopeList({
|
||||
ordering: "scope_name",
|
||||
})
|
||||
.then((propertyMappings: PaginatedSCIMMappingList) => {
|
||||
this.propertyMappings = propertyMappings;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const provider = this.wizard.provider as SCIMProvider | undefined;
|
||||
|
||||
return html`<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||
<ak-text-input
|
||||
name="name"
|
||||
label=${msg("Name")}
|
||||
value=${ifDefined(provider?.name)}
|
||||
required
|
||||
></ak-text-input>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Protocol settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-text-input
|
||||
name="url"
|
||||
label=${msg("URL")}
|
||||
value="${first(provider?.url, "")}"
|
||||
required
|
||||
help=${msg("SCIM base url, usually ends in /v2.")}
|
||||
>
|
||||
</ak-text-input>
|
||||
<ak-text-input
|
||||
name="token"
|
||||
label=${msg("Token")}
|
||||
value="${first(provider?.token, "")}"
|
||||
required
|
||||
help=${msg(
|
||||
"Token to authenticate with. Currently only bearer authentication is supported.",
|
||||
)}
|
||||
>
|
||||
</ak-text-input>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header">${msg("User filtering")}</span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-switch-input
|
||||
name="excludeUsersServiceAccount"
|
||||
?checked=${first(provider?.excludeUsersServiceAccount, true)}
|
||||
label=${msg("Exclude service accounts")}
|
||||
></ak-switch-input>
|
||||
<ak-form-element-horizontal label=${msg("Group")} name="filterGroup">
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<Group[]> => {
|
||||
const args: CoreGroupsListRequest = {
|
||||
ordering: "name",
|
||||
};
|
||||
if (query !== undefined) {
|
||||
args.search = query;
|
||||
}
|
||||
const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList(
|
||||
args,
|
||||
);
|
||||
return groups.results;
|
||||
}}
|
||||
.renderElement=${(group: Group): string => {
|
||||
return group.name;
|
||||
}}
|
||||
.value=${(group: Group | undefined): string | undefined => {
|
||||
return group ? group.pk : undefined;
|
||||
}}
|
||||
.selected=${(group: Group): boolean => {
|
||||
return group.pk === provider?.filterGroup;
|
||||
}}
|
||||
?blankable=${true}
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Only sync users within the selected group.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group ?expanded=${true}>
|
||||
<span slot="header"> ${msg("Attribute mapping")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("User Property Mappings")}
|
||||
?required=${true}
|
||||
name="propertyMappings"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!provider?.propertyMappings) {
|
||||
selected =
|
||||
mapping.managed === "goauthentik.io/providers/scim/user" ||
|
||||
false;
|
||||
} else {
|
||||
selected = Array.from(provider?.propertyMappings).some((su) => {
|
||||
return su == mapping.pk;
|
||||
});
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Property mappings used to user mapping.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Group Property Mappings")}
|
||||
?required=${true}
|
||||
name="propertyMappingsGroup"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.propertyMappings?.results.map((mapping) => {
|
||||
let selected = false;
|
||||
if (!provider?.propertyMappingsGroup) {
|
||||
selected =
|
||||
mapping.managed === "goauthentik.io/providers/scim/group";
|
||||
} else {
|
||||
selected = Array.from(provider?.propertyMappingsGroup).some(
|
||||
(su) => {
|
||||
return su == mapping.pk;
|
||||
},
|
||||
);
|
||||
}
|
||||
return html`<option
|
||||
value=${ifDefined(mapping.pk)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${mapping.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Property mappings used to group creation.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default ApplicationWizardAuthenticationBySCIM;
|
Reference in a new issue