diff --git a/web/src/interfaces/AdminInterface.ts b/web/src/interfaces/AdminInterface.ts index a9e7f4f9f..fd6e87426 100644 --- a/web/src/interfaces/AdminInterface.ts +++ b/web/src/interfaces/AdminInterface.ts @@ -40,6 +40,9 @@ export class AdminInterface extends Interface { ${ID_REGEX})$`]}> ${t`Providers`} + + ${t`Tenants`} + diff --git a/web/src/pages/tenants/TenantForm.ts b/web/src/pages/tenants/TenantForm.ts new file mode 100644 index 000000000..b0c78c780 --- /dev/null +++ b/web/src/pages/tenants/TenantForm.ts @@ -0,0 +1,158 @@ +import { CoreApi, FlowsApi, FlowsInstancesListDesignationEnum, Tenant } from "authentik-api"; +import { t } from "@lingui/macro"; +import { customElement } from "lit-element"; +import { html, TemplateResult } from "lit-html"; +import { DEFAULT_CONFIG } from "../../api/Config"; +import "../../elements/forms/HorizontalFormElement"; +import { first } from "../../utils"; +import { ModelForm } from "../../elements/forms/ModelForm"; +import { until } from "lit-html/directives/until"; + +@customElement("ak-tenant-form") +export class TenantForm extends ModelForm { + + loadInstance(pk: string): Promise { + return new CoreApi(DEFAULT_CONFIG).coreTenantsRetrieve({ + tenantUuid: pk + }); + } + + getSuccessMessage(): string { + if (this.instance) { + return t`Successfully updated tenant.`; + } else { + return t`Successfully created tenant.`; + } + } + + send = (data: Tenant): Promise => { + if (this.instance?.tenantUuid) { + return new CoreApi(DEFAULT_CONFIG).coreTenantsUpdate({ + tenantUuid: this.instance.tenantUuid, + tenantRequest: data + }); + } else { + return new CoreApi(DEFAULT_CONFIG).coreTenantsCreate({ + tenantRequest: data + }); + } + }; + + renderForm(): TemplateResult { + return html`
+ + + + +
+ + +
+

${t`Use this tenant for each domain that doesn't have a dedicated tenant.`}

+
+ + + + ${t`Branding settings`} + +
+ + +

${t`Branding shown in page title and several other places.`}

+
+ + +

${t`Icon shown in sidebar/header and flow executor.`}

+
+
+
+ + + ${t`Default flows`} + +
+ + +

${t`Flow used to authenticate users. If left empty, the first applicable flow sorted by the slug is used.`}

+
+ + +

${t`Flow used to logout. If left empty, the first applicable flow sorted by the slug is used.`}

+
+ + +

${t`Recovery flow. If left empty, the first applicable flow sorted by the slug is used.`}

+
+ + +

${t`If set, users are able to unenroll themselves using this flow. If no flow is set, option is not shown.`}

+
+
+
+
`; + } + +} diff --git a/web/src/pages/tenants/TenantListPage.ts b/web/src/pages/tenants/TenantListPage.ts new file mode 100644 index 000000000..2974218fa --- /dev/null +++ b/web/src/pages/tenants/TenantListPage.ts @@ -0,0 +1,101 @@ +import { t } from "@lingui/macro"; +import { customElement, html, property, TemplateResult } from "lit-element"; +import { AKResponse } from "../../api/Client"; +import { TablePage } from "../../elements/table/TablePage"; + +import "../../elements/forms/DeleteForm"; +import "../../elements/buttons/SpinnerButton"; +import { TableColumn } from "../../elements/table/Table"; +import { PAGE_SIZE } from "../../constants"; +import { CoreApi, Tenant } from "authentik-api"; +import { DEFAULT_CONFIG } from "../../api/Config"; +import "../../elements/forms/ModalForm"; +import "./TenantForm"; + +@customElement("ak-tenant-list") +export class TenantListPage extends TablePage { + searchEnabled(): boolean { + return true; + } + pageTitle(): string { + return t`Tenants`; + } + pageDescription(): string { + return t`Configure visual settings and defaults for different domains.`; + } + pageIcon(): string { + return "pf-icon pf-icon-tenant"; + } + + @property() + order = "domain"; + + apiEndpoint(page: number): Promise> { + return new CoreApi(DEFAULT_CONFIG).coreTenantsList({ + ordering: this.order, + page: page, + pageSize: PAGE_SIZE, + search: this.search || "", + }); + } + + columns(): TableColumn[] { + return [ + new TableColumn(t`Domain`, "domain"), + new TableColumn(t`Default?`, "default"), + new TableColumn(""), + ]; + } + + row(item: Tenant): TemplateResult[] { + return [ + html`${item.domain}`, + html`${item._default ? t`Yes` : t`No`}`, + html` + + + ${t`Update`} + + + ${t`Update Tenant`} + + + + + + { + return new CoreApi(DEFAULT_CONFIG).coreTenantsDestroy({ + tenantUuid: item.tenantUuid + }); + }}> + + `, + ]; + } + + renderToolbar(): TemplateResult { + return html` + + + ${t`Create`} + + + ${t`Create Tenant`} + + + + + + ${super.renderToolbar()} + `; + } +} diff --git a/web/src/pages/user-settings/UserDetailsPage.ts b/web/src/pages/user-settings/UserDetailsPage.ts index 87c77aba7..f729758ba 100644 --- a/web/src/pages/user-settings/UserDetailsPage.ts +++ b/web/src/pages/user-settings/UserDetailsPage.ts @@ -10,13 +10,12 @@ import { CoreApi, User } from "authentik-api"; import { me } from "../../api/Users"; import { FlowURLManager } from "../../api/legacy"; import { ifDefined } from "lit-html/directives/if-defined"; -import { DEFAULT_CONFIG } from "../../api/Config"; +import { DEFAULT_CONFIG, tenant } from "../../api/Config"; import "../../elements/forms/FormElement"; import "../../elements/EmptyState"; import "../../elements/forms/Form"; import "../../elements/forms/HorizontalFormElement"; import { until } from "lit-html/directives/until"; -import { tenant } from "authentik-api/dist/src/api/Config"; @customElement("ak-user-details") export class UserDetailsPage extends LitElement { diff --git a/web/src/routes.ts b/web/src/routes.ts index e3f909576..e159c6d47 100644 --- a/web/src/routes.ts +++ b/web/src/routes.ts @@ -25,10 +25,11 @@ import "./pages/stages/invitation/InvitationListPage"; import "./pages/stages/prompt/PromptListPage"; import "./pages/stages/StageListPage"; import "./pages/system-tasks/SystemTaskListPage"; +import "./pages/tenants/TenantListPage"; import "./pages/tokens/TokenListPage"; +import "./pages/user-settings/UserSettingsPage"; import "./pages/users/UserListPage"; import "./pages/users/UserViewPage"; -import "./pages/user-settings/UserSettingsPage"; export const ROUTES: Route[] = [ // Prevent infinite Shell loops @@ -51,6 +52,7 @@ export const ROUTES: Route[] = [ }), new Route(new RegExp("^/core/property-mappings$"), html``), new Route(new RegExp("^/core/tokens$"), html``), + new Route(new RegExp("^/core/tenants$"), html``), new Route(new RegExp("^/policy/policies$"), html``), new Route(new RegExp("^/identity/groups$"), html``), new Route(new RegExp("^/identity/users$"), html``),