web: add radius to application wizard
This commit continues the application wizard buildout. In this commit are the following changes: - Fixed a width-setting bug in the Makefile `make help` feature (i.e "automate that stuff!") - Added Radius to the list of providers we can offer via the wizard - Added `launchUrl` and `UI Settings` to features of the application page the wizard can find - Changed 'SAML Manual Configuration' to just say "SAML Configuration" - Modified `ak-form-group` to take and honor the `aria-label` property (which in turn makes it easier to target specific forms with unit testing) - Reduced the log level for wdio to 'warn'; 'info' was super-spammy and not helpful. It can be put back with `--logLevel info` from the command line.
This commit is contained in:
parent
f2ba927d34
commit
c05ff4cca1
7
Makefile
7
Makefile
|
@ -28,10 +28,13 @@ CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \
|
|||
|
||||
all: lint-fix lint test gen web ## Lint, build, and test everything
|
||||
|
||||
HELP_WIDTH := $(shell grep -h '^[a-z][^ ]*:.*\#\#' $(MAKEFILE_LIST) 2>/dev/null | \
|
||||
cut -d':' -f1 | awk '{printf "%d\n", length}' | sort -rn | head -1)
|
||||
|
||||
help: ## Show this help
|
||||
@echo "\nSpecify a command. The choices are:\n"
|
||||
@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
||||
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[0;36m%-24s\033[m %s\n", $$1, $$2}' | \
|
||||
@grep -Eh '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
||||
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[0;36m%-$(HELP_WIDTH)s \033[m %s\n", $$1, $$2}' | \
|
||||
sort
|
||||
@echo ""
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ 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 RadiusForm from "./forms/radius.form.js";
|
||||
import { $ } from "@wdio/globals";
|
||||
|
||||
/**
|
||||
|
@ -23,6 +24,7 @@ class ApplicationWizardView extends AdminPage {
|
|||
forwardProxy = ForwardProxyForm;
|
||||
saml = SamlForm;
|
||||
scim = ScimForm;
|
||||
radius = RadiusForm;
|
||||
app = ApplicationForm;
|
||||
|
||||
get wizardTitle() {
|
||||
|
|
|
@ -5,6 +5,14 @@ export class ApplicationForm extends Page {
|
|||
get name() {
|
||||
return $('>>>ak-form-element-horizontal input[name="name"]');
|
||||
}
|
||||
|
||||
get uiSettings() {
|
||||
return $('>>>ak-form-group button[aria-label="UI Settings"]');
|
||||
}
|
||||
|
||||
get launchUrl() {
|
||||
return $('>>>input[name="metaLaunchUrl"]');
|
||||
}
|
||||
}
|
||||
|
||||
export default new ApplicationForm();
|
||||
|
|
13
tests/wdio/test/pageobjects/forms/radius.form.ts
Normal file
13
tests/wdio/test/pageobjects/forms/radius.form.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import Page from "../page.js";
|
||||
|
||||
export class RadiusForm extends Page {
|
||||
async setAuthenticationFlow(selector: string) {
|
||||
await this.searchSelect(
|
||||
'>>>ak-tenanted-flow-search[name="authorizationFlow"] input[type="text"]',
|
||||
"authorizationFlow",
|
||||
`button*=${selector}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default new RadiusForm();
|
|
@ -4,6 +4,8 @@ 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();
|
||||
|
||||
|
@ -17,6 +19,11 @@ async function reachTheProvider(title: string) {
|
|||
await expect(await ApplicationWizardView.wizardTitle).toHaveText("New application");
|
||||
|
||||
await ApplicationWizardView.app.name.setValue(`${title} - ${newPrefix}`);
|
||||
await ApplicationWizardView.app.uiSettings.scrollIntoView();
|
||||
await ApplicationWizardView.app.uiSettings.click();
|
||||
await ApplicationWizardView.app.launchUrl.scrollIntoView();
|
||||
await ApplicationWizardView.app.launchUrl.setValue('http://example.goauthentik.io');
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
return await ApplicationWizardView.pause();
|
||||
}
|
||||
|
@ -29,11 +36,16 @@ async function getCommitMessage() {
|
|||
|
||||
|
||||
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.scrollIntoView();
|
||||
await ApplicationWizardView.ldapProvider.click();
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
|
@ -50,7 +62,9 @@ describe("Configure Applications with the Application Wizard", () => {
|
|||
await reachTheProvider("New Oauth2 Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.oauth2Provider.scrollIntoView();
|
||||
await ApplicationWizardView.oauth2Provider.click();
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
|
@ -69,7 +83,9 @@ describe("Configure Applications with the Application Wizard", () => {
|
|||
await reachTheProvider("New SAML Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.samlProvider.scrollIntoView();
|
||||
await ApplicationWizardView.samlProvider.click();
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
|
@ -89,7 +105,9 @@ describe("Configure Applications with the Application Wizard", () => {
|
|||
await reachTheProvider("New SCIM Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.scimProvider.scrollIntoView();
|
||||
await ApplicationWizardView.scimProvider.click();
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
|
@ -103,10 +121,32 @@ describe("Configure Applications with the Application Wizard", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Should configure a simple Radius Application", async () => {
|
||||
await reachTheProvider("New Radius Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.radiusProvider.scrollIntoView();
|
||||
await ApplicationWizardView.radiusProvider.click();
|
||||
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
||||
await ApplicationWizardView.radius.setAuthenticationFlow(
|
||||
"default-authentication-flow"
|
||||
);
|
||||
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.scrollIntoView();
|
||||
await ApplicationWizardView.proxyProviderProxy.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
@ -129,6 +169,7 @@ describe("Configure Applications with the Application Wizard", () => {
|
|||
await reachTheProvider("New Forward Proxy Application");
|
||||
|
||||
await ApplicationWizardView.providerList.waitForDisplayed();
|
||||
await ApplicationWizardView.proxyProviderForwardsingle.scrollIntoView();
|
||||
await ApplicationWizardView.proxyProviderForwardsingle.click();
|
||||
await ApplicationWizardView.nextButton.click();
|
||||
await ApplicationWizardView.pause();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { Options } from "@wdio/types";
|
||||
import { browser } from "@wdio/globals";
|
||||
|
||||
export const config: Options.Testrunner = {
|
||||
//
|
||||
|
@ -86,7 +87,7 @@ export const config: Options.Testrunner = {
|
|||
// Define all options that are relevant for the WebdriverIO instance here
|
||||
//
|
||||
// Level of logging verbosity: trace | debug | info | warn | error | silent
|
||||
logLevel: "info",
|
||||
logLevel: "warn",
|
||||
//
|
||||
// Set specific log levels per logger
|
||||
// loggers:
|
||||
|
@ -209,8 +210,8 @@ export const config: Options.Testrunner = {
|
|||
* @param {Array.<String>} specs List of spec file paths that are to be run
|
||||
* @param {object} browser instance of created browser/device session
|
||||
*/
|
||||
// before: function (capabilities, specs) {
|
||||
// },
|
||||
before: function (capabilities, specs) {
|
||||
},
|
||||
/**
|
||||
* Runs before a WebdriverIO command gets executed.
|
||||
* @param {string} commandName hook command name
|
||||
|
|
|
@ -72,8 +72,8 @@ export class ApplicationWizardApplicationDetails extends BasePanel {
|
|||
.options=${policyOptions}
|
||||
.value=${this.wizard.app?.policyEngineMode}
|
||||
></ak-radio-input>
|
||||
<ak-form-group>
|
||||
<span slot="header"> ${msg("UI settings")} </span>
|
||||
<ak-form-group aria-label="UI Settings">
|
||||
<span slot="header"> ${msg("UI Settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-text-input
|
||||
name="metaLaunchUrl"
|
||||
|
|
|
@ -8,6 +8,7 @@ import type {
|
|||
ModelRequest,
|
||||
OAuth2ProviderRequest,
|
||||
ProxyProviderRequest,
|
||||
RadiusProviderRequest,
|
||||
SAMLProviderRequest,
|
||||
SCIMProviderRequest,
|
||||
} from "@goauthentik/api";
|
||||
|
@ -59,11 +60,18 @@ const _providerModelsTable: ProviderType[] = [
|
|||
],
|
||||
[
|
||||
"samlprovider",
|
||||
msg("SAML Manual configuration"),
|
||||
msg("SAML Configuration"),
|
||||
msg("Configure SAML provider manually"),
|
||||
() => html`<ak-application-wizard-authentication-by-saml-configuration></ak-application-wizard-authentication-by-saml-configuration>`,
|
||||
ProviderModelEnum.SamlSamlprovider
|
||||
],
|
||||
[
|
||||
"radiusprovider",
|
||||
msg("RADIUS Configuration"),
|
||||
msg("Configure RADIUS provider manually"),
|
||||
() => html`<ak-application-wizard-authentication-by-radius></ak-application-wizard-authentication-by-radius>`,
|
||||
ProviderModelEnum.RadiusRadiusprovider
|
||||
],
|
||||
[
|
||||
"scimprovider",
|
||||
msg("SCIM Manual configuration"),
|
||||
|
@ -109,6 +117,13 @@ const converters = new Map<ProviderModelEnumType, ModelConverter>([
|
|||
...(provider as SCIMProviderRequest),
|
||||
}),
|
||||
],
|
||||
[
|
||||
ProviderModelEnum.RadiusRadiusprovider,
|
||||
(provider: OneOfProvider) => ({
|
||||
providerModel: ProviderModelEnum.RadiusRadiusprovider,
|
||||
...(provider as RadiusProviderRequest),
|
||||
}),
|
||||
],
|
||||
]);
|
||||
|
||||
// Contract enforcement
|
||||
|
|
|
@ -177,7 +177,9 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
|||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
const icon = classMap(this.commitState.icon.reduce((acc, icon) => ({ ...acc, [icon]: true }), {}));
|
||||
const icon = classMap(
|
||||
this.commitState.icon.reduce((acc, icon) => ({ ...acc, [icon]: true }), {}),
|
||||
);
|
||||
|
||||
return html`
|
||||
<div>
|
||||
|
|
|
@ -6,6 +6,7 @@ import "./ldap/ak-application-wizard-authentication-by-ldap";
|
|||
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 "./radius/ak-application-wizard-authentication-by-radius";
|
||||
import "./saml/ak-application-wizard-authentication-by-saml-configuration";
|
||||
import "./scim/ak-application-wizard-authentication-by-scim";
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
||||
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement } from "@lit/reactive-element/decorators.js";
|
||||
import { html } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import { FlowsInstancesListDesignationEnum, RadiusProvider } from "@goauthentik/api";
|
||||
|
||||
import BaseProviderPanel from "../BaseProviderPanel";
|
||||
|
||||
@customElement("ak-application-wizard-authentication-by-radius")
|
||||
export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
||||
render() {
|
||||
const provider = this.wizard.provider as RadiusProvider | 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-element-horizontal
|
||||
label=${msg("Authentication flow")}
|
||||
?required=${true}
|
||||
name="authorizationFlow"
|
||||
>
|
||||
<ak-tenanted-flow-search
|
||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||
.currentFlow=${provider?.authorizationFlow}
|
||||
.tenantFlow=${rootInterface()?.tenant?.flowAuthentication}
|
||||
required
|
||||
></ak-tenanted-flow-search>
|
||||
<p class="pf-c-form__helper-text">${msg("Flow used for users to authenticate.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Protocol settings")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-text-input
|
||||
name="sharedSecret"
|
||||
label=${msg("Shared secret")}
|
||||
value=${first(
|
||||
provider?.sharedSecret,
|
||||
randomString(128, ascii_letters + digits),
|
||||
)}
|
||||
required
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
name="clientNetworks"
|
||||
label=${msg("Client Networks")}
|
||||
value=${first(provider?.clientNetworks, "0.0.0.0/0, ::/0")}
|
||||
required
|
||||
help=${msg(`List of CIDRs (comma-seperated) that clients can connect from. A more specific
|
||||
CIDR will match before a looser one. Clients connecting from a non-specified CIDR
|
||||
will be dropped.`)}
|
||||
></ak-text-input>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default ApplicationWizardAuthenticationByRadius;
|
|
@ -25,6 +25,9 @@ export class FormGroup extends AKElement {
|
|||
@property({ type: Boolean, reflect: true })
|
||||
expanded = false;
|
||||
|
||||
@property({ type: String, attribute: "aria-label", reflect: true })
|
||||
ariaLabel = "Details";
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
|
@ -47,7 +50,7 @@ export class FormGroup extends AKElement {
|
|||
class="pf-c-button pf-m-plain"
|
||||
type="button"
|
||||
aria-expanded="${this.expanded}"
|
||||
aria-label="Details"
|
||||
aria-label=${this.ariaLabel}
|
||||
@click=${() => {
|
||||
this.expanded = !this.expanded;
|
||||
}}
|
||||
|
|
Reference in a new issue