Compare commits
	
		
			6 Commits
		
	
	
		
			dependabot
			...
			policies/p
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b3883f7fbf | |||
| 87c6b0128a | |||
| b243c97916 | |||
| 3f66527521 | |||
| 2f7c258657 | |||
| 917c90374f | 
| @ -1,11 +1,26 @@ | |||||||
| """Expression Policy API""" | """Expression Policy API""" | ||||||
|  |  | ||||||
|  | from drf_spectacular.utils import OpenApiResponse, extend_schema | ||||||
|  | from guardian.shortcuts import get_objects_for_user | ||||||
|  | from rest_framework.decorators import action | ||||||
|  | from rest_framework.fields import CharField | ||||||
|  | from rest_framework.request import Request | ||||||
|  | from rest_framework.response import Response | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.core.api.used_by import UsedByMixin | from authentik.core.api.used_by import UsedByMixin | ||||||
|  | from authentik.events.logs import LogEventSerializer, capture_logs | ||||||
|  | from authentik.policies.api.exec import PolicyTestResultSerializer, PolicyTestSerializer | ||||||
| from authentik.policies.api.policies import PolicySerializer | from authentik.policies.api.policies import PolicySerializer | ||||||
| from authentik.policies.expression.evaluator import PolicyEvaluator | from authentik.policies.expression.evaluator import PolicyEvaluator | ||||||
| from authentik.policies.expression.models import ExpressionPolicy | from authentik.policies.expression.models import ExpressionPolicy | ||||||
|  | from authentik.policies.models import PolicyBinding | ||||||
|  | from authentik.policies.process import PolicyProcess | ||||||
|  | from authentik.policies.types import PolicyRequest | ||||||
|  | from authentik.rbac.decorators import permission_required | ||||||
|  |  | ||||||
|  | LOGGER = get_logger() | ||||||
|  |  | ||||||
|  |  | ||||||
| class ExpressionPolicySerializer(PolicySerializer): | class ExpressionPolicySerializer(PolicySerializer): | ||||||
| @ -30,3 +45,50 @@ class ExpressionPolicyViewSet(UsedByMixin, ModelViewSet): | |||||||
|     filterset_fields = "__all__" |     filterset_fields = "__all__" | ||||||
|     ordering = ["name"] |     ordering = ["name"] | ||||||
|     search_fields = ["name"] |     search_fields = ["name"] | ||||||
|  |  | ||||||
|  |     class ExpressionPolicyTestSerializer(PolicyTestSerializer): | ||||||
|  |         """Expression policy test serializer""" | ||||||
|  |  | ||||||
|  |         expression = CharField() | ||||||
|  |  | ||||||
|  |     @permission_required("authentik_policies.view_policy") | ||||||
|  |     @extend_schema( | ||||||
|  |         request=ExpressionPolicyTestSerializer(), | ||||||
|  |         responses={ | ||||||
|  |             200: PolicyTestResultSerializer(), | ||||||
|  |             400: OpenApiResponse(description="Invalid parameters"), | ||||||
|  |         }, | ||||||
|  |     ) | ||||||
|  |     @action(detail=True, pagination_class=None, filter_backends=[], methods=["POST"]) | ||||||
|  |     def test(self, request: Request, pk: str) -> Response: | ||||||
|  |         """Test policy""" | ||||||
|  |         policy = self.get_object() | ||||||
|  |         test_params = self.ExpressionPolicyTestSerializer(data=request.data) | ||||||
|  |         if not test_params.is_valid(): | ||||||
|  |             return Response(test_params.errors, status=400) | ||||||
|  |  | ||||||
|  |         # User permission check, only allow policy testing for users that are readable | ||||||
|  |         users = get_objects_for_user(request.user, "authentik_core.view_user").filter( | ||||||
|  |             pk=test_params.validated_data["user"].pk | ||||||
|  |         ) | ||||||
|  |         if not users.exists(): | ||||||
|  |             return Response(status=400) | ||||||
|  |  | ||||||
|  |         policy.expression = test_params.validated_data["expression"] | ||||||
|  |  | ||||||
|  |         p_request = PolicyRequest(users.first()) | ||||||
|  |         p_request.debug = True | ||||||
|  |         p_request.set_http_request(self.request) | ||||||
|  |         p_request.context = test_params.validated_data.get("context", {}) | ||||||
|  |  | ||||||
|  |         proc = PolicyProcess(PolicyBinding(policy=policy), p_request, None) | ||||||
|  |         with capture_logs() as logs: | ||||||
|  |             result = proc.execute() | ||||||
|  |         log_messages = [] | ||||||
|  |         for log in logs: | ||||||
|  |             if log.attributes.get("process", "") == "PolicyProcess": | ||||||
|  |                 continue | ||||||
|  |             log_messages.append(LogEventSerializer(log).data) | ||||||
|  |         result.log_messages = log_messages | ||||||
|  |         response = PolicyTestResultSerializer(result) | ||||||
|  |         return Response(response.data) | ||||||
|  | |||||||
							
								
								
									
										52
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								schema.yml
									
									
									
									
									
								
							| @ -12813,6 +12813,43 @@ paths: | |||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/GenericError' |                 $ref: '#/components/schemas/GenericError' | ||||||
|           description: '' |           description: '' | ||||||
|  |   /policies/expression/{policy_uuid}/test/: | ||||||
|  |     post: | ||||||
|  |       operationId: policies_expression_test_create | ||||||
|  |       description: Test policy | ||||||
|  |       parameters: | ||||||
|  |       - in: path | ||||||
|  |         name: policy_uuid | ||||||
|  |         schema: | ||||||
|  |           type: string | ||||||
|  |           format: uuid | ||||||
|  |         description: A UUID string identifying this Expression Policy. | ||||||
|  |         required: true | ||||||
|  |       tags: | ||||||
|  |       - policies | ||||||
|  |       requestBody: | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               $ref: '#/components/schemas/ExpressionPolicyTestRequest' | ||||||
|  |         required: true | ||||||
|  |       security: | ||||||
|  |       - authentik: [] | ||||||
|  |       responses: | ||||||
|  |         '200': | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $ref: '#/components/schemas/PolicyTestResult' | ||||||
|  |           description: '' | ||||||
|  |         '400': | ||||||
|  |           description: Invalid parameters | ||||||
|  |         '403': | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $ref: '#/components/schemas/GenericError' | ||||||
|  |           description: '' | ||||||
|   /policies/expression/{policy_uuid}/used_by/: |   /policies/expression/{policy_uuid}/used_by/: | ||||||
|     get: |     get: | ||||||
|       operationId: policies_expression_used_by_list |       operationId: policies_expression_used_by_list | ||||||
| @ -42280,6 +42317,21 @@ components: | |||||||
|       required: |       required: | ||||||
|       - expression |       - expression | ||||||
|       - name |       - name | ||||||
|  |     ExpressionPolicyTestRequest: | ||||||
|  |       type: object | ||||||
|  |       description: Expression policy test serializer | ||||||
|  |       properties: | ||||||
|  |         user: | ||||||
|  |           type: integer | ||||||
|  |         context: | ||||||
|  |           type: object | ||||||
|  |           additionalProperties: {} | ||||||
|  |         expression: | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|  |       required: | ||||||
|  |       - expression | ||||||
|  |       - user | ||||||
|     ExtraRoleObjectPermission: |     ExtraRoleObjectPermission: | ||||||
|       type: object |       type: object | ||||||
|       description: User permission with additional object-related data |       description: User permission with additional object-related data | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import "@goauthentik/admin/policies/password/PasswordPolicyForm"; | |||||||
| import "@goauthentik/admin/policies/reputation/ReputationPolicyForm"; | import "@goauthentik/admin/policies/reputation/ReputationPolicyForm"; | ||||||
| import "@goauthentik/admin/rbac/ObjectPermissionModal"; | import "@goauthentik/admin/rbac/ObjectPermissionModal"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
|  | import { PFSize } from "@goauthentik/common/enums"; | ||||||
| import { PFColor } from "@goauthentik/elements/Label"; | import { PFColor } from "@goauthentik/elements/Label"; | ||||||
| import "@goauthentik/elements/forms/ConfirmationForm"; | import "@goauthentik/elements/forms/ConfirmationForm"; | ||||||
| import "@goauthentik/elements/forms/DeleteBulkForm"; | import "@goauthentik/elements/forms/DeleteBulkForm"; | ||||||
| @ -21,7 +22,6 @@ import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; | |||||||
| import { msg, str } from "@lit/localize"; | import { msg, str } from "@lit/localize"; | ||||||
| import { TemplateResult, html } from "lit"; | import { TemplateResult, html } from "lit"; | ||||||
| import { customElement, property } from "lit/decorators.js"; | import { customElement, property } from "lit/decorators.js"; | ||||||
| import { ifDefined } from "lit/directives/if-defined.js"; |  | ||||||
|  |  | ||||||
| import { PoliciesApi, Policy } from "@goauthentik/api"; | import { PoliciesApi, Policy } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| @ -71,7 +71,12 @@ export class PolicyListPage extends TablePage<Policy> { | |||||||
|                           ${msg("Warning: Policy is not assigned.")} |                           ${msg("Warning: Policy is not assigned.")} | ||||||
|                       </ak-label>`}`, |                       </ak-label>`}`, | ||||||
|             html`${item.verboseName}`, |             html`${item.verboseName}`, | ||||||
|             html` <ak-forms-modal> |             html` <ak-forms-modal | ||||||
|  |                     size=${item.component === "ak-policy-expression-form" | ||||||
|  |                         ? PFSize.XLarge | ||||||
|  |                         : PFSize.Large} | ||||||
|  |                     ?fullHeight=${item.component === "ak-policy-expression-form"} | ||||||
|  |                 > | ||||||
|                     <span slot="submit"> ${msg("Update")} </span> |                     <span slot="submit"> ${msg("Update")} </span> | ||||||
|                     <span slot="header"> ${msg(str`Update ${item.verboseName}`)} </span> |                     <span slot="header"> ${msg(str`Update ${item.verboseName}`)} </span> | ||||||
|                     <ak-proxy-form |                     <ak-proxy-form | ||||||
| @ -79,7 +84,7 @@ export class PolicyListPage extends TablePage<Policy> { | |||||||
|                         .args=${{ |                         .args=${{ | ||||||
|                             instancePk: item.pk, |                             instancePk: item.pk, | ||||||
|                         }} |                         }} | ||||||
|                         type=${ifDefined(item.component)} |                         type=${item.component} | ||||||
|                     > |                     > | ||||||
|                     </ak-proxy-form> |                     </ak-proxy-form> | ||||||
|                     <button slot="trigger" class="pf-c-button pf-m-plain"> |                     <button slot="trigger" class="pf-c-button pf-m-plain"> | ||||||
|  | |||||||
| @ -87,7 +87,10 @@ export class PolicyWizard extends AKElement { | |||||||
|                             slot=${`type-${type.component}-${type.modelName}`} |                             slot=${`type-${type.component}-${type.modelName}`} | ||||||
|                             .sidebarLabel=${() => msg(str`Create ${type.name}`)} |                             .sidebarLabel=${() => msg(str`Create ${type.name}`)} | ||||||
|                         > |                         > | ||||||
|                             <ak-proxy-form type=${type.component}></ak-proxy-form> |                             <ak-proxy-form | ||||||
|  |                                 ?showPreview=${false} | ||||||
|  |                                 type=${type.component} | ||||||
|  |                             ></ak-proxy-form> | ||||||
|                         </ak-wizard-page-form> |                         </ak-wizard-page-form> | ||||||
|                     `; |                     `; | ||||||
|                 })} |                 })} | ||||||
|  | |||||||
| @ -1,25 +1,64 @@ | |||||||
| import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm"; | import { BasePolicyForm } from "@goauthentik/admin/policies/BasePolicyForm"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { docLink } from "@goauthentik/common/global"; | import { docLink } from "@goauthentik/common/global"; | ||||||
|  | import { me } from "@goauthentik/common/users"; | ||||||
| import { first } from "@goauthentik/common/utils"; | import { first } from "@goauthentik/common/utils"; | ||||||
| import "@goauthentik/elements/CodeMirror"; | import "@goauthentik/elements/CodeMirror"; | ||||||
| import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror"; | import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror"; | ||||||
|  | import "@goauthentik/elements/EmptyState"; | ||||||
| import "@goauthentik/elements/forms/FormGroup"; | import "@goauthentik/elements/forms/FormGroup"; | ||||||
| import "@goauthentik/elements/forms/HorizontalFormElement"; | import "@goauthentik/elements/forms/HorizontalFormElement"; | ||||||
|  | import YAML from "yaml"; | ||||||
|  |  | ||||||
| import { msg } from "@lit/localize"; | import { msg } from "@lit/localize"; | ||||||
| import { TemplateResult, html } from "lit"; | import { CSSResult, TemplateResult, html, nothing } from "lit"; | ||||||
| import { customElement } from "lit/decorators.js"; | import { customElement, property, state } from "lit/decorators.js"; | ||||||
| import { ifDefined } from "lit/directives/if-defined.js"; | import { ifDefined } from "lit/directives/if-defined.js"; | ||||||
|  |  | ||||||
| import { ExpressionPolicy, PoliciesApi } from "@goauthentik/api"; | import PFTitle from "@patternfly/patternfly/components/Title/title.css"; | ||||||
|  | import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; | ||||||
|  | import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css"; | ||||||
|  |  | ||||||
|  | import { | ||||||
|  |     CoreApi, | ||||||
|  |     CoreUsersListRequest, | ||||||
|  |     ExpressionPolicy, | ||||||
|  |     PoliciesApi, | ||||||
|  |     PolicyTestResult, | ||||||
|  |     ResponseError, | ||||||
|  |     SessionUser, | ||||||
|  |     User, | ||||||
|  |     ValidationErrorFromJSON, | ||||||
|  | } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| @customElement("ak-policy-expression-form") | @customElement("ak-policy-expression-form") | ||||||
| export class ExpressionPolicyForm extends BasePolicyForm<ExpressionPolicy> { | export class ExpressionPolicyForm extends BasePolicyForm<ExpressionPolicy> { | ||||||
|     loadInstance(pk: string): Promise<ExpressionPolicy> { |     @property({ type: Boolean }) | ||||||
|         return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionRetrieve({ |     showPreview = true; | ||||||
|  |  | ||||||
|  |     @state() | ||||||
|  |     preview?: PolicyTestResult; | ||||||
|  |  | ||||||
|  |     @state() | ||||||
|  |     previewError?: string[]; | ||||||
|  |  | ||||||
|  |     @state() | ||||||
|  |     user?: SessionUser; | ||||||
|  |  | ||||||
|  |     @state() | ||||||
|  |     previewLoading = false; | ||||||
|  |  | ||||||
|  |     static get styles(): CSSResult[] { | ||||||
|  |         return super.styles.concat(PFGrid, PFStack, PFTitle); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async loadInstance(pk: string): Promise<ExpressionPolicy> { | ||||||
|  |         const policy = await new PoliciesApi(DEFAULT_CONFIG).policiesExpressionRetrieve({ | ||||||
|             policyUuid: pk, |             policyUuid: pk, | ||||||
|         }); |         }); | ||||||
|  |         this.user = await me(); | ||||||
|  |         await this.refreshPreview(policy); | ||||||
|  |         return policy; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async send(data: ExpressionPolicy): Promise<ExpressionPolicy> { |     async send(data: ExpressionPolicy): Promise<ExpressionPolicy> { | ||||||
| @ -35,10 +74,196 @@ export class ExpressionPolicyForm extends BasePolicyForm<ExpressionPolicy> { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     _shouldRefresh = false; | ||||||
|  |     _timer = 0; | ||||||
|  |  | ||||||
|  |     connectedCallback(): void { | ||||||
|  |         super.connectedCallback(); | ||||||
|  |         if (!this.showPreview) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // Only check if we should update once a second, to prevent spamming API requests | ||||||
|  |         // when many fields are edited | ||||||
|  |         const minUpdateDelay = 1000; | ||||||
|  |         this._timer = setInterval(() => { | ||||||
|  |             if (this._shouldRefresh) { | ||||||
|  |                 this.refreshPreview(); | ||||||
|  |                 this._shouldRefresh = false; | ||||||
|  |             } | ||||||
|  |         }, minUpdateDelay) as unknown as number; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     disconnectedCallback(): void { | ||||||
|  |         super.disconnectedCallback(); | ||||||
|  |         if (!this.showPreview) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         clearTimeout(this._timer); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async refreshPreview(policy?: ExpressionPolicy): Promise<void> { | ||||||
|  |         if (!policy) { | ||||||
|  |             policy = this.serializeForm(); | ||||||
|  |             if (!policy) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         this.previewLoading = true; | ||||||
|  |         try { | ||||||
|  |             interface testpolicy { | ||||||
|  |                 expression: string; | ||||||
|  |                 user?: number; | ||||||
|  |                 context?: { [key: string]: unknown }; | ||||||
|  |             } | ||||||
|  |             const tp = policy as unknown as testpolicy; | ||||||
|  |             this.preview = await new PoliciesApi(DEFAULT_CONFIG).policiesExpressionTestCreate({ | ||||||
|  |                 expressionPolicyTestRequest: { | ||||||
|  |                     expression: tp.expression, | ||||||
|  |                     user: tp.user || this.user?.user.pk || 0, | ||||||
|  |                     context: tp.context || {}, | ||||||
|  |                 }, | ||||||
|  |                 policyUuid: this.instancePk || "", | ||||||
|  |             }); | ||||||
|  |             this.previewError = undefined; | ||||||
|  |         } catch (exc) { | ||||||
|  |             const errorMessage = ValidationErrorFromJSON( | ||||||
|  |                 await (exc as ResponseError).response.json(), | ||||||
|  |             ); | ||||||
|  |             this.previewError = errorMessage.nonFieldErrors; | ||||||
|  |         } finally { | ||||||
|  |             this.previewLoading = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     renderForm(): TemplateResult { |     renderForm(): TemplateResult { | ||||||
|  |         return html`<div class="pf-l-grid pf-m-gutter"> | ||||||
|  |             <div class="pf-l-grid__item pf-m-6-col pf-l-stack"> | ||||||
|  |                 <div class="pf-c-form pf-m-horizontal pf-l-stack__item"> | ||||||
|  |                     ${this.renderEditForm()} | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="pf-l-grid__item pf-m-6-col">${this.renderPreview()}</div> | ||||||
|  |         </div> `; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     renderPreview(): TemplateResult { | ||||||
|  |         return html` | ||||||
|  |             <div class="pf-l-grid pf-m-gutter"> | ||||||
|  |                 <div class="pf-c-card pf-l-grid__item pf-m-12-col"> | ||||||
|  |                     <div class="pf-c-card__title">${msg("Test parameters")}</div> | ||||||
|  |                     <div class="pf-c-card__body pf-c-form pf-m-horizontal"> | ||||||
|  |                         <ak-form-element-horizontal label=${msg("User")} name="user"> | ||||||
|  |                             <ak-search-select | ||||||
|  |                                 .fetchObjects=${async (query?: string): Promise<User[]> => { | ||||||
|  |                                     const args: CoreUsersListRequest = { | ||||||
|  |                                         ordering: "username", | ||||||
|  |                                     }; | ||||||
|  |                                     if (query !== undefined) { | ||||||
|  |                                         args.search = query; | ||||||
|  |                                     } | ||||||
|  |                                     const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList( | ||||||
|  |                                         args, | ||||||
|  |                                     ); | ||||||
|  |                                     return users.results; | ||||||
|  |                                 }} | ||||||
|  |                                 .renderElement=${(user: User): string => { | ||||||
|  |                                     return user.username; | ||||||
|  |                                 }} | ||||||
|  |                                 .renderDescription=${(user: User): TemplateResult => { | ||||||
|  |                                     return html`${user.name}`; | ||||||
|  |                                 }} | ||||||
|  |                                 .value=${(user: User | undefined): number | undefined => { | ||||||
|  |                                     return user?.pk; | ||||||
|  |                                 }} | ||||||
|  |                                 .selected=${(user: User): boolean => { | ||||||
|  |                                     return this.user?.user.pk === user.pk; | ||||||
|  |                                 }} | ||||||
|  |                             > | ||||||
|  |                             </ak-search-select> | ||||||
|  |                         </ak-form-element-horizontal> | ||||||
|  |                         <ak-form-element-horizontal label=${msg("Context")} name="context"> | ||||||
|  |                             <ak-codemirror mode=${CodeMirrorMode.YAML} value=${YAML.stringify({})}> | ||||||
|  |                             </ak-codemirror> | ||||||
|  |                         </ak-form-element-horizontal> | ||||||
|  |                     </div> | ||||||
|  |                     <div class="pf-c-card__footer"> | ||||||
|  |                         <button | ||||||
|  |                             class="pf-c-button pf-m-primary" | ||||||
|  |                             @click=${() => { | ||||||
|  |                                 this.refreshPreview(); | ||||||
|  |                             }} | ||||||
|  |                         > | ||||||
|  |                             ${msg("Execute")} | ||||||
|  |                         </button> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="pf-c-card pf-l-grid__item pf-m-12-col"> | ||||||
|  |                     <div class="pf-c-card__title">${msg("Test results")}</div> | ||||||
|  |                     ${this.previewLoading | ||||||
|  |                         ? html`<ak-empty-state loading></ak-empty-state>` | ||||||
|  |                         : html`<div class="pf-c-card__body pf-c-form pf-m-horizontal"> | ||||||
|  |                               <ak-form-element-horizontal label=${msg("Passing")}> | ||||||
|  |                                   <div class="pf-c-form__group-label"> | ||||||
|  |                                       <div class="c-form__horizontal-group"> | ||||||
|  |                                           <span class="pf-c-form__label-text"> | ||||||
|  |                                               <ak-status-label | ||||||
|  |                                                   ?good=${this.preview?.passing} | ||||||
|  |                                               ></ak-status-label> | ||||||
|  |                                           </span> | ||||||
|  |                                       </div> | ||||||
|  |                                   </div> | ||||||
|  |                               </ak-form-element-horizontal> | ||||||
|  |                               <ak-form-element-horizontal label=${msg("Messages")}> | ||||||
|  |                                   <div class="pf-c-form__group-label"> | ||||||
|  |                                       <div class="c-form__horizontal-group"> | ||||||
|  |                                           <ul> | ||||||
|  |                                               ${(this.preview?.messages || []).length > 0 | ||||||
|  |                                                   ? this.preview?.messages?.map((m) => { | ||||||
|  |                                                         return html`<li> | ||||||
|  |                                                             <span class="pf-c-form__label-text" | ||||||
|  |                                                                 >${m}</span | ||||||
|  |                                                             > | ||||||
|  |                                                         </li>`; | ||||||
|  |                                                     }) | ||||||
|  |                                                   : html`<li> | ||||||
|  |                                                         <span class="pf-c-form__label-text">-</span> | ||||||
|  |                                                     </li>`} | ||||||
|  |                                           </ul> | ||||||
|  |                                       </div> | ||||||
|  |                                   </div> | ||||||
|  |                               </ak-form-element-horizontal> | ||||||
|  |  | ||||||
|  |                               <ak-form-element-horizontal label=${msg("Log messages")}> | ||||||
|  |                                   <div class="pf-c-form__group-label"> | ||||||
|  |                                       <div class="c-form__horizontal-group"> | ||||||
|  |                                           <dl class="pf-c-description-list pf-m-horizontal"> | ||||||
|  |                                               <ak-log-viewer | ||||||
|  |                                                   .logs=${this.preview?.logMessages} | ||||||
|  |                                               ></ak-log-viewer> | ||||||
|  |                                           </dl> | ||||||
|  |                                       </div> | ||||||
|  |                                   </div> | ||||||
|  |                               </ak-form-element-horizontal> | ||||||
|  |                           </div>`} | ||||||
|  |                 </div> | ||||||
|  |                 ${this.previewError | ||||||
|  |                     ? html` | ||||||
|  |                           <div class="pf-c-card pf-l-grid__item pf-m-12-col"> | ||||||
|  |                               <div class="pf-c-card__body">${msg("Preview errors")}</div> | ||||||
|  |                               <div class="pf-c-card__body"> | ||||||
|  |                                   ${this.previewError.map((err) => html`<pre>${err}</pre>`)} | ||||||
|  |                               </div> | ||||||
|  |                           </div> | ||||||
|  |                       ` | ||||||
|  |                     : nothing} | ||||||
|  |             </div> | ||||||
|  |         `; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     renderEditForm(): TemplateResult { | ||||||
|         return html` <span> |         return html` <span> | ||||||
|                 ${msg( |                 ${msg( | ||||||
|                     "Executes the python snippet to determine whether to allow or deny a request.", |                     "Executes the Python snippet to determine whether to allow or deny a request.", | ||||||
|                 )} |                 )} | ||||||
|             </span> |             </span> | ||||||
|             <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name"> |             <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name"> | ||||||
| @ -80,6 +305,9 @@ export class ExpressionPolicyForm extends BasePolicyForm<ExpressionPolicy> { | |||||||
|                         <ak-codemirror |                         <ak-codemirror | ||||||
|                             mode=${CodeMirrorMode.Python} |                             mode=${CodeMirrorMode.Python} | ||||||
|                             value="${ifDefined(this.instance?.expression)}" |                             value="${ifDefined(this.instance?.expression)}" | ||||||
|  |                             @change=${() => { | ||||||
|  |                                 this._shouldRefresh = true; | ||||||
|  |                             }} | ||||||
|                         > |                         > | ||||||
|                         </ak-codemirror> |                         </ak-codemirror> | ||||||
|                         <p class="pf-c-form__helper-text"> |                         <p class="pf-c-form__helper-text"> | ||||||
|  | |||||||
| @ -20,8 +20,11 @@ export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderStatusBanner() { |     renderStatusBanner() { | ||||||
|  |         if (!this.licenseSummary) { | ||||||
|  |             return nothing; | ||||||
|  |         } | ||||||
|         // Check if we're in the correct interface to render a banner |         // Check if we're in the correct interface to render a banner | ||||||
|         switch (this.licenseSummary.status) { |         switch (this.licenseSummary?.status) { | ||||||
|             // user warning is both on admin interface and user interface |             // user warning is both on admin interface and user interface | ||||||
|             case LicenseSummaryStatusEnum.LimitExceededUser: |             case LicenseSummaryStatusEnum.LimitExceededUser: | ||||||
|                 if ( |                 if ( | ||||||
| @ -46,7 +49,7 @@ export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) { | |||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|         let message = ""; |         let message = ""; | ||||||
|         switch (this.licenseSummary.status) { |         switch (this.licenseSummary?.status) { | ||||||
|             case LicenseSummaryStatusEnum.LimitExceededAdmin: |             case LicenseSummaryStatusEnum.LimitExceededAdmin: | ||||||
|             case LicenseSummaryStatusEnum.LimitExceededUser: |             case LicenseSummaryStatusEnum.LimitExceededUser: | ||||||
|                 message = msg( |                 message = msg( | ||||||
| @ -83,13 +86,16 @@ export class EnterpriseStatusBanner extends WithLicenseSummary(AKElement) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderFlagBanner() { |     renderFlagBanner() { | ||||||
|  |         if (!this.licenseSummary) { | ||||||
|  |             return nothing; | ||||||
|  |         } | ||||||
|         return html` |         return html` | ||||||
|             ${this.licenseSummary.licenseFlags.includes(LicenseFlagsEnum.Trial) |             ${this.licenseSummary?.licenseFlags.includes(LicenseFlagsEnum.Trial) | ||||||
|                 ? html`<div class="pf-c-banner pf-m-sticky pf-m-gold"> |                 ? html`<div class="pf-c-banner pf-m-sticky pf-m-gold"> | ||||||
|                       ${msg("This authentik instance uses a Trial license.")} |                       ${msg("This authentik instance uses a Trial license.")} | ||||||
|                   </div>` |                   </div>` | ||||||
|                 : nothing} |                 : nothing} | ||||||
|             ${this.licenseSummary.licenseFlags.includes(LicenseFlagsEnum.NonProduction) |             ${this.licenseSummary?.licenseFlags.includes(LicenseFlagsEnum.NonProduction) | ||||||
|                 ? html`<div class="pf-c-banner pf-m-sticky pf-m-gold"> |                 ? html`<div class="pf-c-banner pf-m-sticky pf-m-gold"> | ||||||
|                       ${msg("This authentik instance uses a Non-production license.")} |                       ${msg("This authentik instance uses a Non-production license.")} | ||||||
|                   </div>` |                   </div>` | ||||||
|  | |||||||
| @ -40,6 +40,9 @@ export class ModalButton extends AKElement { | |||||||
|     @property() |     @property() | ||||||
|     size: PFSize = PFSize.Large; |     size: PFSize = PFSize.Large; | ||||||
|  |  | ||||||
|  |     @property({ type: Boolean }) | ||||||
|  |     fullHeight = false; | ||||||
|  |  | ||||||
|     @property({ type: Boolean }) |     @property({ type: Boolean }) | ||||||
|     open = false; |     open = false; | ||||||
|  |  | ||||||
| @ -69,6 +72,9 @@ export class ModalButton extends AKElement { | |||||||
|                 .pf-c-modal-box.pf-m-xl { |                 .pf-c-modal-box.pf-m-xl { | ||||||
|                     --pf-c-modal-box--Width: calc(1.5 * var(--pf-c-modal-box--m-lg--lg--MaxWidth)); |                     --pf-c-modal-box--Width: calc(1.5 * var(--pf-c-modal-box--m-lg--lg--MaxWidth)); | ||||||
|                 } |                 } | ||||||
|  |                 :host([fullHeight]) .pf-c-modal-box { | ||||||
|  |                     height: 100%; | ||||||
|  |                 } | ||||||
|             `, |             `, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -14,7 +14,20 @@ import { LogEvent, LogLevelEnum } from "@goauthentik/api"; | |||||||
| @customElement("ak-log-viewer") | @customElement("ak-log-viewer") | ||||||
| export class LogViewer extends Table<LogEvent> { | export class LogViewer extends Table<LogEvent> { | ||||||
|     @property({ attribute: false }) |     @property({ attribute: false }) | ||||||
|     logs?: LogEvent[] = []; |     set logs(val: LogEvent[]) { | ||||||
|  |         this.data = { | ||||||
|  |             pagination: { | ||||||
|  |                 next: 0, | ||||||
|  |                 previous: 0, | ||||||
|  |                 count: val.length || 0, | ||||||
|  |                 current: 1, | ||||||
|  |                 totalPages: 1, | ||||||
|  |                 startIndex: 1, | ||||||
|  |                 endIndex: val.length || 0, | ||||||
|  |             }, | ||||||
|  |             results: val, | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     expandable = true; |     expandable = true; | ||||||
|     paginated = false; |     paginated = false; | ||||||
| @ -24,7 +37,8 @@ export class LogViewer extends Table<LogEvent> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async apiEndpoint(): Promise<PaginatedResponse<LogEvent>> { |     async apiEndpoint(): Promise<PaginatedResponse<LogEvent>> { | ||||||
|         return { |         return ( | ||||||
|  |             this.data || { | ||||||
|                 pagination: { |                 pagination: { | ||||||
|                     next: 0, |                     next: 0, | ||||||
|                     previous: 0, |                     previous: 0, | ||||||
| @ -35,7 +49,8 @@ export class LogViewer extends Table<LogEvent> { | |||||||
|                     endIndex: this.logs?.length || 0, |                     endIndex: this.logs?.length || 0, | ||||||
|                 }, |                 }, | ||||||
|                 results: this.logs || [], |                 results: this.logs || [], | ||||||
|         }; |             } | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderEmpty(): TemplateResult { |     renderEmpty(): TemplateResult { | ||||||
|  | |||||||
| @ -38,6 +38,10 @@ export abstract class ModelForm<T, PKT extends string | number> extends Form<T> | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     get instancePk(): PKT | undefined { | ||||||
|  |         return this._instancePk; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private _instancePk?: PKT; |     private _instancePk?: PKT; | ||||||
|  |  | ||||||
|     // Keep track if we've loaded the model instance |     // Keep track if we've loaded the model instance | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	