core: app entitlements (#12090)
* core: initial app entitlements Signed-off-by: Jens Langhammer <jens@goauthentik.io> * base off of pbm Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add tests and oauth2 Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add to proxy Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rewrite to use bindings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make policy bindings form and list more customizable Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * double fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * refine permissions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add missing rbac modal to app entitlements Signed-off-by: Jens Langhammer <jens@goauthentik.io> * separate scope for app entitlements Signed-off-by: Jens Langhammer <jens@goauthentik.io> * include entitlements mapping in proxy Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add API validation to prevent policies from being bound to entitlements Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make preview Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add initial docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove duplicate docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import "@goauthentik/admin/applications/ApplicationAuthorizeChart";
|
||||
import "@goauthentik/admin/applications/ApplicationCheckAccessForm";
|
||||
import "@goauthentik/admin/applications/ApplicationForm";
|
||||
import "@goauthentik/admin/applications/entitlements/ApplicationEntitlementPage";
|
||||
import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
@ -301,6 +302,28 @@ export class ApplicationViewPage extends AKElement {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-app-entitlements"
|
||||
data-tab-title="${msg("Application entitlements")}"
|
||||
>
|
||||
<div slot="header" class="pf-c-banner pf-m-info">
|
||||
${msg("Application entitlements are in preview.")}
|
||||
<a href="mailto:hello+feature/app-ent@goauthentik.io"
|
||||
>${msg("Send us feedback!")}</a
|
||||
>
|
||||
</div>
|
||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">
|
||||
${msg(
|
||||
"These entitlements can be used to configure user access in this application.",
|
||||
)}
|
||||
</div>
|
||||
<ak-application-entitlements-list .app=${this.application.pk}>
|
||||
</ak-application-entitlements-list>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
slot="page-policy-bindings"
|
||||
data-tab-title="${msg("Policy / Group / User Bindings")}"
|
||||
|
@ -0,0 +1,89 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
|
||||
import { ApplicationEntitlement, CoreApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-application-entitlement-form")
|
||||
export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement, string> {
|
||||
async loadInstance(pk: string): Promise<ApplicationEntitlement> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsRetrieve({
|
||||
pbmUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
@property()
|
||||
targetPk?: string;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance?.pbmUuid) {
|
||||
return msg("Successfully updated entitlement.");
|
||||
} else {
|
||||
return msg("Successfully created entitlement.");
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [...super.styles, PFContent];
|
||||
}
|
||||
|
||||
send(data: ApplicationEntitlement): Promise<unknown> {
|
||||
if (this.targetPk) {
|
||||
data.app = this.targetPk;
|
||||
}
|
||||
if (this.instance?.pbmUuid) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsUpdate({
|
||||
pbmUuid: this.instance.pbmUuid || "",
|
||||
applicationEntitlementRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsCreate({
|
||||
applicationEntitlementRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.name, "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Attributes")}
|
||||
?required=${false}
|
||||
name="attributes"
|
||||
>
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.YAML}
|
||||
value="${YAML.stringify(first(this.instance?.attributes, {}))}"
|
||||
>
|
||||
</ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Set custom attributes using YAML or JSON.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-application-entitlement-form": ApplicationEntitlementForm;
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
import "@goauthentik/admin/applications/entitlements/ApplicationEntitlementForm";
|
||||
import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||
import { PolicyBindingCheckTarget } from "@goauthentik/admin/policies/utils";
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionModal";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PFSize } from "@goauthentik/common/enums";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
import "@goauthentik/elements/forms/ProxyForm";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
ApplicationEntitlement,
|
||||
CoreApi,
|
||||
RbacPermissionsAssignedByUsersListModelEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-application-entitlements-list")
|
||||
export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
|
||||
@property()
|
||||
app?: string;
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
expandable = true;
|
||||
|
||||
order = "order";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<ApplicationEntitlement>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
app: this.app || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [new TableColumn(msg("Name"), "name"), new TableColumn(msg("Actions"))];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Application entitlement(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
.usedBy=${(item: ApplicationEntitlement) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsUsedByList({
|
||||
pbmUuid: item.pbmUuid || "",
|
||||
});
|
||||
}}
|
||||
.delete=${(item: ApplicationEntitlement) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsDestroy({
|
||||
pbmUuid: item.pbmUuid || "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: ApplicationEntitlement): TemplateResult[] {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${msg("Update")} </span>
|
||||
<span slot="header"> ${msg("Update Entitlement")} </span>
|
||||
<ak-application-entitlement-form
|
||||
slot="form"
|
||||
.instancePk=${item.pbmUuid}
|
||||
targetPk=${ifDefined(this.app)}
|
||||
>
|
||||
</ak-application-entitlement-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-plain">
|
||||
<pf-tooltip position="top" content=${msg("Edit")}>
|
||||
<i class="fas fa-edit"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
<ak-rbac-object-permission-modal
|
||||
model=${RbacPermissionsAssignedByUsersListModelEnum.CoreApplicationentitlement}
|
||||
objectPk=${item.pbmUuid}
|
||||
>
|
||||
</ak-rbac-object-permission-modal>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: ApplicationEntitlement): TemplateResult {
|
||||
return html` <td></td>
|
||||
<td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<div class="pf-c-content">
|
||||
<p>
|
||||
${msg(
|
||||
"These bindings control which users have access to this entitlement.",
|
||||
)}
|
||||
</p>
|
||||
<ak-bound-policies-list
|
||||
.target=${item.pbmUuid}
|
||||
.allowedTypes=${[
|
||||
PolicyBindingCheckTarget.group,
|
||||
PolicyBindingCheckTarget.user,
|
||||
]}
|
||||
>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
</td>`;
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state
|
||||
header=${msg("No app entitlements created.")}
|
||||
icon="pf-icon-module"
|
||||
>
|
||||
<div slot="body">
|
||||
${msg(
|
||||
"This application does currently not have any application entitlement defined.",
|
||||
)}
|
||||
</div>
|
||||
<div slot="primary"></div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Entitlement")} </span>
|
||||
<ak-application-entitlement-form slot="form" targetPk=${ifDefined(this.app)}>
|
||||
</ak-application-entitlement-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${msg("Create entitlement")}
|
||||
</button>
|
||||
</ak-forms-modal> `;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-application-roles-list": ApplicationEntitlementsPage;
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
import "@goauthentik/admin/groups/GroupForm";
|
||||
import "@goauthentik/admin/policies/PolicyBindingForm";
|
||||
import { PolicyBindingNotice } from "@goauthentik/admin/policies/PolicyBindingForm";
|
||||
import "@goauthentik/admin/policies/PolicyWizard";
|
||||
import {
|
||||
PolicyBindingCheckTarget,
|
||||
PolicyBindingCheckTargetToLabel,
|
||||
} from "@goauthentik/admin/policies/utils";
|
||||
import "@goauthentik/admin/users/UserForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PFSize } from "@goauthentik/common/enums.js";
|
||||
@ -13,7 +18,7 @@ import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { TemplateResult, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@ -24,14 +29,25 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
@property()
|
||||
target?: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
policyOnly = false;
|
||||
@property({ type: Array })
|
||||
allowedTypes: PolicyBindingCheckTarget[] = [
|
||||
PolicyBindingCheckTarget.group,
|
||||
PolicyBindingCheckTarget.user,
|
||||
PolicyBindingCheckTarget.policy,
|
||||
];
|
||||
|
||||
@property({ type: Array })
|
||||
typeNotices: PolicyBindingNotice[] = [];
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
|
||||
order = "order";
|
||||
|
||||
get allowedTypesLabel(): string {
|
||||
return this.allowedTypes.map((ct) => PolicyBindingCheckTargetToLabel(ct)).join(" / ");
|
||||
}
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<PolicyBinding>> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
@ -42,7 +58,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Order"), "order"),
|
||||
new TableColumn(msg("Policy / User / Group")),
|
||||
new TableColumn(this.allowedTypesLabel),
|
||||
new TableColumn(msg("Enabled"), "enabled"),
|
||||
new TableColumn(msg("Timeout"), "timeout"),
|
||||
new TableColumn(msg("Actions")),
|
||||
@ -121,7 +137,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
return [
|
||||
{ key: msg("Order"), value: item.order.toString() },
|
||||
{
|
||||
key: msg("Policy / User / Group"),
|
||||
key: this.allowedTypesLabel,
|
||||
value: this.getPolicyUserGroupRowLabel(item),
|
||||
},
|
||||
];
|
||||
@ -156,8 +172,9 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
.instancePk=${item.pk}
|
||||
.allowedTypes=${this.allowedTypes}
|
||||
.typeNotices=${this.typeNotices}
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary">
|
||||
@ -183,7 +200,8 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
.allowedTypes=${this.allowedTypes}
|
||||
.typeNotices=${this.typeNotices}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
@ -196,22 +214,25 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`<ak-policy-wizard
|
||||
createText=${msg("Create and bind Policy")}
|
||||
?showBindingPage=${true}
|
||||
bindingTarget=${ifDefined(this.target)}
|
||||
></ak-policy-wizard>
|
||||
return html`${this.allowedTypes.includes(PolicyBindingCheckTarget.policy)
|
||||
? html`<ak-policy-wizard
|
||||
createText=${msg("Create and bind Policy")}
|
||||
?showBindingPage=${true}
|
||||
bindingTarget=${ifDefined(this.target)}
|
||||
></ak-policy-wizard>`
|
||||
: nothing}
|
||||
<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Binding")} </span>
|
||||
<ak-policy-binding-form
|
||||
slot="form"
|
||||
targetPk=${ifDefined(this.target)}
|
||||
?policyOnly=${this.policyOnly}
|
||||
.allowedTypes=${this.allowedTypes}
|
||||
.typeNotices=${this.typeNotices}
|
||||
>
|
||||
</ak-policy-binding-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||
${msg("Bind existing policy/group/user")}
|
||||
${msg(str`Bind existing ${this.allowedTypesLabel}`)}
|
||||
</button>
|
||||
</ak-forms-modal> `;
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
import {
|
||||
PolicyBindingCheckTarget,
|
||||
PolicyBindingCheckTargetToLabel,
|
||||
} from "@goauthentik/admin/policies/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first, groupBy } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-toggle-group";
|
||||
@ -7,7 +11,7 @@ import "@goauthentik/elements/forms/Radio";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult } from "lit";
|
||||
import { CSSResult, nothing } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
@ -25,11 +29,7 @@ import {
|
||||
User,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
enum target {
|
||||
policy = "policy",
|
||||
group = "group",
|
||||
user = "user",
|
||||
}
|
||||
export type PolicyBindingNotice = { type: PolicyBindingCheckTarget; notice: string };
|
||||
|
||||
@customElement("ak-policy-binding-form")
|
||||
export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
@ -38,13 +38,13 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
policyBindingUuid: pk,
|
||||
});
|
||||
if (binding?.policyObj) {
|
||||
this.policyGroupUser = target.policy;
|
||||
this.policyGroupUser = PolicyBindingCheckTarget.policy;
|
||||
}
|
||||
if (binding?.groupObj) {
|
||||
this.policyGroupUser = target.group;
|
||||
this.policyGroupUser = PolicyBindingCheckTarget.group;
|
||||
}
|
||||
if (binding?.userObj) {
|
||||
this.policyGroupUser = target.user;
|
||||
this.policyGroupUser = PolicyBindingCheckTarget.user;
|
||||
}
|
||||
this.defaultOrder = await this.getOrder();
|
||||
return binding;
|
||||
@ -54,10 +54,17 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
targetPk?: string;
|
||||
|
||||
@state()
|
||||
policyGroupUser: target = target.policy;
|
||||
policyGroupUser: PolicyBindingCheckTarget = PolicyBindingCheckTarget.policy;
|
||||
|
||||
@property({ type: Boolean })
|
||||
policyOnly = false;
|
||||
@property({ type: Array })
|
||||
allowedTypes: PolicyBindingCheckTarget[] = [
|
||||
PolicyBindingCheckTarget.group,
|
||||
PolicyBindingCheckTarget.user,
|
||||
PolicyBindingCheckTarget.policy,
|
||||
];
|
||||
|
||||
@property({ type: Array })
|
||||
typeNotices: PolicyBindingNotice[] = [];
|
||||
|
||||
@state()
|
||||
defaultOrder = 0;
|
||||
@ -74,20 +81,26 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
return [...super.styles, PFContent];
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
// Overwrite the default for policyGroupUser with the first allowed type,
|
||||
// as this function is called when the correct parameters are set
|
||||
this.policyGroupUser = this.allowedTypes[0];
|
||||
}
|
||||
|
||||
send(data: PolicyBinding): Promise<unknown> {
|
||||
if (this.targetPk) {
|
||||
data.target = this.targetPk;
|
||||
}
|
||||
switch (this.policyGroupUser) {
|
||||
case target.policy:
|
||||
case PolicyBindingCheckTarget.policy:
|
||||
data.user = null;
|
||||
data.group = null;
|
||||
break;
|
||||
case target.group:
|
||||
case PolicyBindingCheckTarget.group:
|
||||
data.policy = null;
|
||||
data.user = null;
|
||||
break;
|
||||
case target.user:
|
||||
case PolicyBindingCheckTarget.user:
|
||||
data.policy = null;
|
||||
data.group = null;
|
||||
break;
|
||||
@ -122,13 +135,18 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
renderModeSelector(): TemplateResult {
|
||||
return html` <ak-toggle-group
|
||||
value=${this.policyGroupUser}
|
||||
@ak-toggle=${(ev: CustomEvent<{ value: target }>) => {
|
||||
@ak-toggle=${(ev: CustomEvent<{ value: PolicyBindingCheckTarget }>) => {
|
||||
this.policyGroupUser = ev.detail.value;
|
||||
}}
|
||||
>
|
||||
<option value=${target.policy}>${msg("Policy")}</option>
|
||||
<option value=${target.group}>${msg("Group")}</option>
|
||||
<option value=${target.user}>${msg("User")}</option>
|
||||
${Object.keys(PolicyBindingCheckTarget).map((ct) => {
|
||||
if (this.allowedTypes.includes(ct as PolicyBindingCheckTarget)) {
|
||||
return html`<option value=${ct}>
|
||||
${PolicyBindingCheckTargetToLabel(ct as PolicyBindingCheckTarget)}
|
||||
</option>`;
|
||||
}
|
||||
return nothing;
|
||||
})}
|
||||
</ak-toggle-group>`;
|
||||
}
|
||||
|
||||
@ -139,7 +157,7 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Policy")}
|
||||
name="policy"
|
||||
?hidden=${this.policyGroupUser !== target.policy}
|
||||
?hidden=${this.policyGroupUser !== PolicyBindingCheckTarget.policy}
|
||||
>
|
||||
<ak-search-select
|
||||
.groupBy=${(items: Policy[]) => {
|
||||
@ -169,11 +187,16 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
?blankable=${true}
|
||||
>
|
||||
</ak-search-select>
|
||||
${this.typeNotices
|
||||
.filter(({ type }) => type === PolicyBindingCheckTarget.policy)
|
||||
.map((msg) => {
|
||||
return html`<p class="pf-c-form__helper-text">${msg.notice}</p>`;
|
||||
})}
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Group")}
|
||||
name="group"
|
||||
?hidden=${this.policyGroupUser !== target.group}
|
||||
?hidden=${this.policyGroupUser !== PolicyBindingCheckTarget.group}
|
||||
>
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<Group[]> => {
|
||||
@ -201,18 +224,16 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
?blankable=${true}
|
||||
>
|
||||
</ak-search-select>
|
||||
${this.policyOnly
|
||||
? html`<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Group mappings can only be checked if a user is already logged in when trying to access this source.",
|
||||
)}
|
||||
</p>`
|
||||
: html``}
|
||||
${this.typeNotices
|
||||
.filter(({ type }) => type === PolicyBindingCheckTarget.group)
|
||||
.map((msg) => {
|
||||
return html`<p class="pf-c-form__helper-text">${msg.notice}</p>`;
|
||||
})}
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("User")}
|
||||
name="user"
|
||||
?hidden=${this.policyGroupUser !== target.user}
|
||||
?hidden=${this.policyGroupUser !== PolicyBindingCheckTarget.user}
|
||||
>
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<User[]> => {
|
||||
@ -240,13 +261,11 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
?blankable=${true}
|
||||
>
|
||||
</ak-search-select>
|
||||
${this.policyOnly
|
||||
? html`<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"User mappings can only be checked if a user is already logged in when trying to access this source.",
|
||||
)}
|
||||
</p>`
|
||||
: html``}
|
||||
${this.typeNotices
|
||||
.filter(({ type }) => type === PolicyBindingCheckTarget.user)
|
||||
.map((msg) => {
|
||||
return html`<p class="pf-c-form__helper-text">${msg.notice}</p>`;
|
||||
})}
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</div>
|
||||
|
18
web/src/admin/policies/utils.ts
Normal file
18
web/src/admin/policies/utils.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
export enum PolicyBindingCheckTarget {
|
||||
policy = "policy",
|
||||
group = "group",
|
||||
user = "user",
|
||||
}
|
||||
|
||||
export function PolicyBindingCheckTargetToLabel(ct: PolicyBindingCheckTarget): string {
|
||||
switch (ct) {
|
||||
case PolicyBindingCheckTarget.group:
|
||||
return msg("Group");
|
||||
case PolicyBindingCheckTarget.user:
|
||||
return msg("User");
|
||||
case PolicyBindingCheckTarget.policy:
|
||||
return msg("Policy");
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
|
||||
import "@goauthentik/admin/sources/oauth/OAuthSourceDiagram";
|
||||
import "@goauthentik/admin/sources/oauth/OAuthSourceForm";
|
||||
import { sourceBindingTypeNotices } from "@goauthentik/admin/sources/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
@ -240,7 +241,10 @@ export class OAuthSourceViewPage extends AKElement {
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
<ak-bound-policies-list
|
||||
.target=${this.source.pk}
|
||||
.typeNotices=${sourceBindingTypeNotices()}
|
||||
>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
|
||||
import "@goauthentik/admin/sources/plex/PlexSourceForm";
|
||||
import { sourceBindingTypeNotices } from "@goauthentik/admin/sources/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
@ -130,7 +131,10 @@ export class PlexSourceViewPage extends AKElement {
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
<ak-bound-policies-list
|
||||
.target=${this.source.pk}
|
||||
.typeNotices=${sourceBindingTypeNotices()}
|
||||
>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
|
||||
import "@goauthentik/admin/sources/saml/SAMLSourceForm";
|
||||
import { sourceBindingTypeNotices } from "@goauthentik/admin/sources/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
@ -207,7 +208,10 @@ export class SAMLSourceViewPage extends AKElement {
|
||||
)}
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
<ak-bound-policies-list .target=${this.source.pk} ?policyOnly=${true}>
|
||||
<ak-bound-policies-list
|
||||
.target=${this.source.pk}
|
||||
.typeNotices=${sourceBindingTypeNotices()}
|
||||
>
|
||||
</ak-bound-policies-list>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { PolicyBindingCheckTarget } from "@goauthentik/admin/policies/utils";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
|
||||
export function renderSourceIcon(name: string, iconUrl: string | undefined | null): TemplateResult {
|
||||
@ -11,3 +14,20 @@ export function renderSourceIcon(name: string, iconUrl: string | undefined | nul
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
export function sourceBindingTypeNotices() {
|
||||
return [
|
||||
{
|
||||
type: PolicyBindingCheckTarget.group,
|
||||
notice: msg(
|
||||
"Group mappings can only be checked if a user is already logged in when trying to access this source.",
|
||||
),
|
||||
},
|
||||
{
|
||||
type: PolicyBindingCheckTarget.user,
|
||||
notice: msg(
|
||||
"User mappings can only be checked if a user is already logged in when trying to access this source.",
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
Reference in New Issue
Block a user