From f8ba623fc15b88d17894202292dc8ef0e91af150 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 16 Feb 2021 20:34:15 +0100 Subject: [PATCH] web: add more related links, add policy/user/group support for bindings --- authentik/flows/transfer/common.py | 2 +- authentik/flows/transfer/importer.py | 2 + authentik/policies/api.py | 6 +- swagger.yaml | 332 +++++++++++++++++- web/src/api/PolicyBindings.ts | 6 +- .../elements/policies/BoundPoliciesList.ts | 16 +- web/src/elements/utils/LoadingState.ts | 24 ++ .../pages/applications/ApplicationViewPage.ts | 30 +- web/src/pages/flows/FlowViewPage.ts | 5 - web/src/pages/providers/ProviderViewPage.ts | 11 +- web/src/routes.ts | 2 +- web/src/utils.ts | 10 +- 12 files changed, 398 insertions(+), 48 deletions(-) create mode 100644 web/src/elements/utils/LoadingState.ts diff --git a/authentik/flows/transfer/common.py b/authentik/flows/transfer/common.py index c66dc705e..9c022dffe 100644 --- a/authentik/flows/transfer/common.py +++ b/authentik/flows/transfer/common.py @@ -11,7 +11,7 @@ from authentik.lib.sentry import SentryIgnoredException def get_attrs(obj: SerializerModel) -> Dict[str, Any]: """Get object's attributes via their serializer, and covert it to a normal dict""" data = dict(obj.serializer(obj).data) - to_remove = ("policies", "stages", "pk", "background") + to_remove = ("policies", "stages", "pk", "background", "group", "user") for to_remove_name in to_remove: if to_remove_name in data: data.pop(to_remove_name) diff --git a/authentik/flows/transfer/importer.py b/authentik/flows/transfer/importer.py index c825c3e8c..2b804f394 100644 --- a/authentik/flows/transfer/importer.py +++ b/authentik/flows/transfer/importer.py @@ -82,6 +82,8 @@ class FlowImporter: main_query = Q(pk=attrs["pk"]) sub_query = Q() for identifier, value in attrs.items(): + if isinstance(value, dict): + continue if identifier == "pk": continue sub_query &= Q(**{identifier: value}) diff --git a/authentik/policies/api.py b/authentik/policies/api.py index 4f43fe927..c83eab3ce 100644 --- a/authentik/policies/api.py +++ b/authentik/policies/api.py @@ -99,15 +99,12 @@ class PolicyBindingSerializer(ModelSerializer): required=True, ) - policy_obj = PolicySerializer(read_only=True, source="policy") - class Meta: model = PolicyBinding fields = [ "pk", "policy", - "policy_obj", "group", "user", "target", @@ -115,12 +112,13 @@ class PolicyBindingSerializer(ModelSerializer): "order", "timeout", ] + depth = 2 class PolicyBindingViewSet(ModelViewSet): """PolicyBinding Viewset""" - queryset = PolicyBinding.objects.all() + queryset = PolicyBinding.objects.all().select_related("policy", "target", "group", "user") serializer_class = PolicyBindingSerializer filterset_fields = ["policy", "target", "enabled", "order", "timeout"] search_fields = ["policy__name"] diff --git a/swagger.yaml b/swagger.yaml index 9db897ffe..a3864bb24 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -8643,21 +8643,327 @@ definitions: format: uuid readOnly: true policy: - title: Policy - type: string - format: uuid - x-nullable: true - policy_obj: - $ref: '#/definitions/Policy' + description: Policies which specify if a user is authorized to use an Application. + Can be overridden by other types to add other fields, more logic, etc. + type: object + properties: + policy_uuid: + title: Policy uuid + type: string + format: uuid + readOnly: true + created: + title: Created + type: string + format: date-time + readOnly: true + last_updated: + title: Last updated + type: string + format: date-time + readOnly: true + name: + title: Name + type: string + x-nullable: true + execution_logging: + title: Execution logging + description: When this option is enabled, all executions of this policy + will be logged. By default, only execution errors are logged. + type: boolean + readOnly: true group: - title: Group - type: string - format: uuid - x-nullable: true + description: Custom Group model which supports a basic hierarchy + required: + - name + type: object + properties: + group_uuid: + title: Group uuid + type: string + format: uuid + readOnly: true + name: + title: Name + type: string + maxLength: 80 + minLength: 1 + is_superuser: + title: Is superuser + description: Users added to this group will be superusers. + type: boolean + attributes: + title: Attributes + type: object + parent: + description: Custom Group model which supports a basic hierarchy + required: + - name + - parent + type: object + properties: + group_uuid: + title: Group uuid + type: string + format: uuid + readOnly: true + name: + title: Name + type: string + maxLength: 80 + minLength: 1 + is_superuser: + title: Is superuser + description: Users added to this group will be superusers. + type: boolean + attributes: + title: Attributes + type: object + parent: + title: Parent + type: string + format: uuid + readOnly: true + readOnly: true user: - title: User - type: integer - x-nullable: true + description: Custom User model to allow easier adding o f user-based settings + required: + - password + - username + - name + type: object + properties: + id: + title: ID + type: integer + readOnly: true + password: + title: Password + type: string + maxLength: 128 + minLength: 1 + last_login: + title: Last login + type: string + format: date-time + x-nullable: true + username: + title: Username + description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ + only. + type: string + pattern: ^[\w.@+-]+$ + maxLength: 150 + minLength: 1 + first_name: + title: First name + type: string + maxLength: 150 + last_name: + title: Last name + type: string + maxLength: 150 + email: + title: Email address + type: string + format: email + maxLength: 254 + is_active: + title: Active + description: Designates whether this user should be treated as active. + Unselect this instead of deleting accounts. + type: boolean + date_joined: + title: Date joined + type: string + format: date-time + uuid: + title: Uuid + type: string + format: uuid + readOnly: true + name: + title: Name + description: User's display name. + type: string + minLength: 1 + password_change_date: + title: Password change date + type: string + format: date-time + readOnly: true + attributes: + title: Attributes + type: object + groups: + description: '' + type: array + items: + description: Groups are a generic way of categorizing users to apply + permissions, or some other label, to those users. A user can belong + to any number of groups. A user in a group automatically has all the + permissions granted to that group. For example, if the group 'Site + editors' has the permission can_edit_home_page, any user in that group + will have that permission. Beyond permissions, groups are a convenient + way to categorize users to apply some label, or extended functionality, + to them. For example, you could create a group 'Special users', and + you could write code that would do special things to those users -- + such as giving them access to a members-only portion of your site, + or sending them members-only email messages. + required: + - name + type: object + properties: + id: + title: ID + type: integer + readOnly: true + name: + title: Name + type: string + maxLength: 150 + minLength: 1 + permissions: + type: array + items: + type: integer + uniqueItems: true + readOnly: true + user_permissions: + description: '' + type: array + items: + description: "The permissions system provides a way to assign permissions\ + \ to specific users and groups of users. The permission system is\ + \ used by the Django admin site, but may also be useful in your own\ + \ code. The Django admin site uses permissions as follows: - The \"\ + add\" permission limits the user's ability to view the \"add\" form\ + \ and add an object. - The \"change\" permission limits a user's ability\ + \ to view the change list, view the \"change\" form and change an\ + \ object. - The \"delete\" permission limits the ability to delete\ + \ an object. - The \"view\" permission limits the ability to view\ + \ an object. Permissions are set globally per type of object, not\ + \ per specific object instance. It is possible to say \"Mary may change\ + \ news stories,\" but it's not currently possible to say \"Mary may\ + \ change news stories, but only the ones she created herself\" or\ + \ \"Mary may only change news stories that have a certain status or\ + \ publication date.\" The permissions listed above are automatically\ + \ created for each model." + required: + - name + - codename + - content_type + type: object + properties: + id: + title: ID + type: integer + readOnly: true + name: + title: Name + type: string + maxLength: 255 + minLength: 1 + codename: + title: Codename + type: string + maxLength: 100 + minLength: 1 + content_type: + title: Content type + type: integer + readOnly: true + sources: + description: '' + type: array + items: + description: Base Authentication source, i.e. an OAuth Provider, SAML + Remote or LDAP Server + required: + - name + - slug + type: object + properties: + pbm_uuid: + title: Pbm uuid + type: string + format: uuid + readOnly: true + name: + title: Name + description: Source's display Name. + type: string + minLength: 1 + slug: + title: Slug + description: Internal source name, used in URLs. + type: string + format: slug + pattern: ^[-a-zA-Z0-9_]+$ + maxLength: 50 + minLength: 1 + enabled: + title: Enabled + type: boolean + authentication_flow: + title: Authentication flow + description: Flow to use when authenticating existing users. + type: string + format: uuid + x-nullable: true + enrollment_flow: + title: Enrollment flow + description: Flow to use when enrolling new users. + type: string + format: uuid + x-nullable: true + policies: + type: array + items: + type: string + format: uuid + readOnly: true + uniqueItems: true + property_mappings: + type: array + items: + type: string + format: uuid + uniqueItems: true + readOnly: true + ak_groups: + description: '' + type: array + items: + description: Custom Group model which supports a basic hierarchy + required: + - name + - parent + type: object + properties: + group_uuid: + title: Group uuid + type: string + format: uuid + readOnly: true + name: + title: Name + type: string + maxLength: 80 + minLength: 1 + is_superuser: + title: Is superuser + description: Users added to this group will be superusers. + type: boolean + attributes: + title: Attributes + type: object + parent: + title: Parent + type: string + format: uuid + readOnly: true + readOnly: true target: title: Target type: string diff --git a/web/src/api/PolicyBindings.ts b/web/src/api/PolicyBindings.ts index fda494443..b8bc45f0d 100644 --- a/web/src/api/PolicyBindings.ts +++ b/web/src/api/PolicyBindings.ts @@ -1,10 +1,14 @@ import { DefaultClient, AKResponse, QueryArguments } from "./Client"; +import { Group } from "./Groups"; import { Policy } from "./Policies"; +import { User } from "./Users"; export class PolicyBinding { pk: string; policy: string; - policy_obj: Policy; + policy_obj?: Policy; + group?: Group; + user?: User; target: string; enabled: boolean; order: number; diff --git a/web/src/elements/policies/BoundPoliciesList.ts b/web/src/elements/policies/BoundPoliciesList.ts index a51eea0c2..56f6d4cef 100644 --- a/web/src/elements/policies/BoundPoliciesList.ts +++ b/web/src/elements/policies/BoundPoliciesList.ts @@ -24,7 +24,7 @@ export class BoundPoliciesList extends Table { columns(): TableColumn[] { return [ - new TableColumn("Policy"), + new TableColumn("Policy / User / Group"), new TableColumn("Enabled", "enabled"), new TableColumn("Order", "order"), new TableColumn("Timeout", "timeout"), @@ -32,9 +32,21 @@ export class BoundPoliciesList extends Table { ]; } + getPolicyUserGroupRow(item: PolicyBinding): string { + if (item.policy_obj) { + return gettext(`Policy ${item.policy_obj.name}`); + } else if (item.group) { + return gettext(`Group ${item.group.name}`); + } else if (item.user) { + return gettext(`User ${item.user.name}`); + } else { + return gettext(``); + } + } + row(item: PolicyBinding): TemplateResult[] { return [ - html`${item.policy_obj.name}`, + html`${this.getPolicyUserGroupRow(item)}`, html`${item.enabled ? "Yes" : "No"}`, html`${item.order}`, html`${item.timeout}`, diff --git a/web/src/elements/utils/LoadingState.ts b/web/src/elements/utils/LoadingState.ts new file mode 100644 index 000000000..0e59f8bdf --- /dev/null +++ b/web/src/elements/utils/LoadingState.ts @@ -0,0 +1,24 @@ +import { commands } from "codemirror"; +import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element"; +import { COMMON_STYLES } from "../../common/styles"; +import { SpinnerSize } from "../Spinner"; + +@customElement("ak-loading-state") +export class LoadingState extends LitElement { + + static get styles(): CSSResult[] { + return COMMON_STYLES; + } + + render(): TemplateResult { + return html`
+
+
+
+ +
+
+
+
`; + } +} diff --git a/web/src/pages/applications/ApplicationViewPage.ts b/web/src/pages/applications/ApplicationViewPage.ts index 76f6dfae8..7aa69605b 100644 --- a/web/src/pages/applications/ApplicationViewPage.ts +++ b/web/src/pages/applications/ApplicationViewPage.ts @@ -9,6 +9,7 @@ import "../../elements/AdminLoginsChart"; import "../../elements/buttons/ModalButton"; import "../../elements/buttons/SpinnerButton"; import "../../elements/policies/BoundPoliciesList"; +import "../../elements/utils/LoadingState"; @customElement("ak-application-view") export class ApplicationViewPage extends LitElement { @@ -37,7 +38,7 @@ export class ApplicationViewPage extends LitElement { render(): TemplateResult { if (!this.application) { - return html``; + return html``;; } return html`
@@ -49,7 +50,7 @@ export class ApplicationViewPage extends LitElement {
-
+
diff --git a/web/src/pages/flows/FlowViewPage.ts b/web/src/pages/flows/FlowViewPage.ts index 15bd2df95..51814e3ef 100644 --- a/web/src/pages/flows/FlowViewPage.ts +++ b/web/src/pages/flows/FlowViewPage.ts @@ -13,11 +13,6 @@ import "./FlowDiagram"; @customElement("ak-flow-view") export class FlowViewPage extends LitElement { - @property() - set args(value: { [key: string]: string }) { - this.flowSlug = value.slug; - } - @property() set flowSlug(value: string) { Flow.get(value).then((flow) => (this.flow = flow)); diff --git a/web/src/pages/providers/ProviderViewPage.ts b/web/src/pages/providers/ProviderViewPage.ts index 55a8bc6c6..c37a8234f 100644 --- a/web/src/pages/providers/ProviderViewPage.ts +++ b/web/src/pages/providers/ProviderViewPage.ts @@ -4,7 +4,6 @@ import { COMMON_STYLES } from "../../common/styles"; import "../../elements/buttons/ModalButton"; import "../../elements/buttons/SpinnerButton"; -import { SpinnerSize } from "../../elements/Spinner"; import "./SAMLProviderViewPage"; import "./OAuth2ProviderViewPage"; @@ -27,15 +26,7 @@ export class ProviderViewPage extends LitElement { render(): TemplateResult { if (!this.provider) { - return html`
-
-
-
- -
-
-
-
`; + return html``;; } switch (this.provider?.object_type) { case "saml": diff --git a/web/src/routes.ts b/web/src/routes.ts index 84ef1ad37..a56d75510 100644 --- a/web/src/routes.ts +++ b/web/src/routes.ts @@ -36,7 +36,7 @@ export const ROUTES: Route[] = [ return html``; }), new Route(new RegExp(`^/flows/(?${SLUG_REGEX})$`)).then((args) => { - return html``; + return html``; }), new Route(new RegExp("^/events/log$"), html``), new Route(new RegExp(`^/events/log/(?${UUID_REGEX})$`)).then((args) => { diff --git a/web/src/utils.ts b/web/src/utils.ts index 2e5697717..bdb0544b9 100644 --- a/web/src/utils.ts +++ b/web/src/utils.ts @@ -47,15 +47,7 @@ export function htmlFromString(...strings: string[]): TemplateResult { export function loading(v: T, actual: TemplateResult): TemplateResult { if (!v) { - return html`
-
-
-
- -
-
-
-
`; + return html``; } return actual; }