Compare commits
6 Commits
providers/
...
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,18 +37,20 @@ export class LogViewer extends Table<LogEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async apiEndpoint(): Promise<PaginatedResponse<LogEvent>> {
|
async apiEndpoint(): Promise<PaginatedResponse<LogEvent>> {
|
||||||
return {
|
return (
|
||||||
pagination: {
|
this.data || {
|
||||||
next: 0,
|
pagination: {
|
||||||
previous: 0,
|
next: 0,
|
||||||
count: this.logs?.length || 0,
|
previous: 0,
|
||||||
current: 1,
|
count: this.logs?.length || 0,
|
||||||
totalPages: 1,
|
current: 1,
|
||||||
startIndex: 1,
|
totalPages: 1,
|
||||||
endIndex: this.logs?.length || 0,
|
startIndex: 1,
|
||||||
},
|
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