Compare commits

...

2 Commits

Author SHA1 Message Date
ad9b5e98ba honor order in api and web
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-17 17:22:06 +02:00
e4a21c824a policies: constraint only one type
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-04-17 17:13:39 +02:00
8 changed files with 141 additions and 1 deletions

View File

@ -78,6 +78,7 @@ class PolicyBindingSerializer(ModelSerializer):
"negate", "negate",
"enabled", "enabled",
"order", "order",
"honor_order",
"timeout", "timeout",
"failure_result", "failure_result",
] ]
@ -110,7 +111,16 @@ class PolicyBindingFilter(FilterSet):
class Meta: class Meta:
model = PolicyBinding model = PolicyBinding
fields = ["policy", "policy__isnull", "target", "target_in", "enabled", "order", "timeout"] fields = [
"policy",
"policy__isnull",
"target",
"target_in",
"enabled",
"order",
"honor_order",
"timeout",
]
class PolicyBindingViewSet(UsedByMixin, ModelViewSet): class PolicyBindingViewSet(UsedByMixin, ModelViewSet):

View File

@ -0,0 +1,40 @@
# Generated by Django 5.1.8 on 2025-04-17 15:13
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0047_delete_oldauthenticatedsession"),
("authentik_policies", "0011_policybinding_failure_result_and_more"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddConstraint(
model_name="policybinding",
constraint=models.CheckConstraint(
condition=models.Q(
models.Q(
("policy_id__isnull", False),
("group_id__isnull", True),
("user_id__isnull", True),
),
models.Q(
("group_id__isnull", False),
("policy_id__isnull", True),
("user_id__isnull", True),
),
models.Q(
("user_id__isnull", False),
("policy_id__isnull", True),
("group_id__isnull", True),
),
_connector="OR",
),
name="authentik_policies_policybinding_only_one_type",
),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 5.1.8 on 2025-04-17 15:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_policies", "0012_policybinding_authentik_policies_policybinding_only_one_type"),
]
operations = [
migrations.AddField(
model_name="policybinding",
name="honor_order",
field=models.BooleanField(
default=False, help_text="Honor order when evaluating policies."
),
),
]

View File

@ -3,6 +3,7 @@
from uuid import uuid4 from uuid import uuid4
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
@ -100,6 +101,10 @@ class PolicyBinding(SerializerModel):
) )
order = models.IntegerField() order = models.IntegerField()
honor_order = models.BooleanField(
default=False,
help_text=_("Honor order when evaluating policies."),
)
def passes(self, request: PolicyRequest) -> PolicyResult: def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if request passes this PolicyBinding, check policy, group or user""" """Check if request passes this PolicyBinding, check policy, group or user"""
@ -158,6 +163,28 @@ class PolicyBinding(SerializerModel):
models.Index(fields=["user"]), models.Index(fields=["user"]),
models.Index(fields=["target"]), models.Index(fields=["target"]),
] ]
constraints = (
models.CheckConstraint(
condition=(
(
Q(policy_id__isnull=False)
& Q(group_id__isnull=True)
& Q(user_id__isnull=True)
)
| (
Q(group_id__isnull=False)
& Q(policy_id__isnull=True)
& Q(user_id__isnull=True)
)
| (
Q(user_id__isnull=False)
& Q(policy_id__isnull=True)
& Q(group_id__isnull=True)
)
),
name="%(app_label)s_%(class)s_only_one_type",
),
)
class Policy(SerializerModel, CreatedUpdatedModel): class Policy(SerializerModel, CreatedUpdatedModel):

View File

@ -5623,6 +5623,11 @@
"maximum": 2147483647, "maximum": 2147483647,
"title": "Order" "title": "Order"
}, },
"honor_order": {
"type": "boolean",
"title": "Honor order",
"description": "Honor order when evaluating policies."
},
"timeout": { "timeout": {
"type": "integer", "type": "integer",
"minimum": 0, "minimum": 0,

View File

@ -12092,6 +12092,10 @@ paths:
name: enabled name: enabled
schema: schema:
type: boolean type: boolean
- in: query
name: honor_order
schema:
type: boolean
- in: query - in: query
name: order name: order
schema: schema:
@ -53311,6 +53315,9 @@ components:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
honor_order:
type: boolean
description: Honor order when evaluating policies.
timeout: timeout:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
@ -54880,6 +54887,9 @@ components:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
honor_order:
type: boolean
description: Honor order when evaluating policies.
timeout: timeout:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
@ -54922,6 +54932,9 @@ components:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
honor_order:
type: boolean
description: Honor order when evaluating policies.
timeout: timeout:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
@ -59183,6 +59196,9 @@ components:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
honor_order:
type: boolean
description: Honor order when evaluating policies.
timeout: timeout:
type: integer type: integer
maximum: 2147483647 maximum: 2147483647

View File

@ -61,6 +61,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
new TableColumn(this.allowedTypesLabel), new TableColumn(this.allowedTypesLabel),
new TableColumn(msg("Enabled"), "enabled"), new TableColumn(msg("Enabled"), "enabled"),
new TableColumn(msg("Timeout"), "timeout"), new TableColumn(msg("Timeout"), "timeout"),
new TableColumn(msg("Honor order"), "honor_order"),
new TableColumn(msg("Actions")), new TableColumn(msg("Actions")),
]; ];
} }
@ -165,6 +166,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
html`${this.getPolicyUserGroupRow(item)}`, html`${this.getPolicyUserGroupRow(item)}`,
html`<ak-status-label type="warning" ?good=${item.enabled}></ak-status-label>`, html`<ak-status-label type="warning" ?good=${item.enabled}></ak-status-label>`,
html`${item.timeout}`, html`${item.timeout}`,
html`<ak-status-label type="info" ?good=${item.honorOrder}></ak-status-label>`,
html` ${this.getObjectEditButton(item)} html` ${this.getObjectEditButton(item)}
<ak-forms-modal size=${PFSize.Medium}> <ak-forms-modal size=${PFSize.Medium}>
<span slot="submit"> ${msg("Update")} </span> <span slot="submit"> ${msg("Update")} </span>

View File

@ -310,6 +310,26 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
required required
/> />
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="honorOrder">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.honorOrder, false)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label">${msg("Honor order")}</span>
</label>
<p class="pf-c-form__helper-text">
${msg(
"Honor the order of policies. Use if policies must be evaluated sequentially following the specified order. May impact performance.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("Timeout")} ?required=${true} name="timeout"> <ak-form-element-horizontal label=${msg("Timeout")} ?required=${true} name="timeout">
<input <input
type="number" type="number"