policies: configurable engine mode (#682)
* policies: add policy_engine_mode field, defaults to MODE_ALL Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * *: add policy_engine_mode to API Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * *: add policy_engine_mode to forms Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * policies: update default for new objects Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * docs: add to release notes Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
da5de30d7b
commit
46f4493f04
|
@ -52,6 +52,7 @@ class ApplicationSerializer(ModelSerializer):
|
||||||
"meta_icon",
|
"meta_icon",
|
||||||
"meta_description",
|
"meta_description",
|
||||||
"meta_publisher",
|
"meta_publisher",
|
||||||
|
"policy_engine_mode",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer):
|
||||||
"object_type",
|
"object_type",
|
||||||
"verbose_name",
|
"verbose_name",
|
||||||
"verbose_name_plural",
|
"verbose_name_plural",
|
||||||
|
"policy_engine_mode",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ from authentik.events.models import (
|
||||||
NotificationTransportError,
|
NotificationTransportError,
|
||||||
)
|
)
|
||||||
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
||||||
from authentik.policies.engine import PolicyEngine, PolicyEngineMode
|
from authentik.policies.engine import PolicyEngine
|
||||||
from authentik.policies.models import PolicyBinding
|
from authentik.policies.models import PolicyBinding, PolicyEngineMode
|
||||||
from authentik.root.celery import CELERY_APP
|
from authentik.root.celery import CELERY_APP
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
@ -60,7 +60,7 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
|
||||||
LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
|
LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
|
||||||
user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
|
user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
|
||||||
policy_engine = PolicyEngine(trigger, user)
|
policy_engine = PolicyEngine(trigger, user)
|
||||||
policy_engine.mode = PolicyEngineMode.MODE_OR
|
policy_engine.mode = PolicyEngineMode.MODE_ANY
|
||||||
policy_engine.empty_result = False
|
policy_engine.empty_result = False
|
||||||
policy_engine.use_cache = False
|
policy_engine.use_cache = False
|
||||||
policy_engine.request.context["event"] = event
|
policy_engine.request.context["event"] = event
|
||||||
|
|
|
@ -23,7 +23,7 @@ class FlowStageBindingSerializer(ModelSerializer):
|
||||||
"evaluate_on_plan",
|
"evaluate_on_plan",
|
||||||
"re_evaluate_policies",
|
"re_evaluate_policies",
|
||||||
"order",
|
"order",
|
||||||
"policies",
|
"policy_engine_mode",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ class FlowSerializer(ModelSerializer):
|
||||||
"stages",
|
"stages",
|
||||||
"policies",
|
"policies",
|
||||||
"cache_count",
|
"cache_count",
|
||||||
|
"policy_engine_mode",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ class FlowStageBindingForm(forms.ModelForm):
|
||||||
"evaluate_on_plan",
|
"evaluate_on_plan",
|
||||||
"re_evaluate_policies",
|
"re_evaluate_policies",
|
||||||
"order",
|
"order",
|
||||||
|
"policy_engine_mode",
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
"name": forms.TextInput(),
|
"name": forms.TextInput(),
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
"""authentik policy engine"""
|
"""authentik policy engine"""
|
||||||
from enum import Enum
|
|
||||||
from multiprocessing import Pipe, current_process
|
from multiprocessing import Pipe, current_process
|
||||||
from multiprocessing.connection import Connection
|
from multiprocessing.connection import Connection
|
||||||
from typing import Iterator, Optional
|
from typing import Iterator, Optional
|
||||||
|
@ -11,7 +10,12 @@ from sentry_sdk.tracing import Span
|
||||||
from structlog.stdlib import BoundLogger, get_logger
|
from structlog.stdlib import BoundLogger, get_logger
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel
|
from authentik.policies.models import (
|
||||||
|
Policy,
|
||||||
|
PolicyBinding,
|
||||||
|
PolicyBindingModel,
|
||||||
|
PolicyEngineMode,
|
||||||
|
)
|
||||||
from authentik.policies.process import PolicyProcess, cache_key
|
from authentik.policies.process import PolicyProcess, cache_key
|
||||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||||
|
|
||||||
|
@ -35,13 +39,6 @@ class PolicyProcessInfo:
|
||||||
self.result = None
|
self.result = None
|
||||||
|
|
||||||
|
|
||||||
class PolicyEngineMode(Enum):
|
|
||||||
"""Decide how results of multiple policies should be combined."""
|
|
||||||
|
|
||||||
MODE_AND = "and"
|
|
||||||
MODE_OR = "or"
|
|
||||||
|
|
||||||
|
|
||||||
class PolicyEngine:
|
class PolicyEngine:
|
||||||
"""Orchestrate policy checking, launch tasks and return result"""
|
"""Orchestrate policy checking, launch tasks and return result"""
|
||||||
|
|
||||||
|
@ -63,7 +60,7 @@ class PolicyEngine:
|
||||||
self, pbm: PolicyBindingModel, user: User, request: HttpRequest = None
|
self, pbm: PolicyBindingModel, user: User, request: HttpRequest = None
|
||||||
):
|
):
|
||||||
self.logger = get_logger().bind()
|
self.logger = get_logger().bind()
|
||||||
self.mode = PolicyEngineMode.MODE_AND
|
self.mode = pbm.policy_engine_mode
|
||||||
# For backwards compatibility, set empty_result to true
|
# For backwards compatibility, set empty_result to true
|
||||||
# objects with no policies attached will pass.
|
# objects with no policies attached will pass.
|
||||||
self.empty_result = True
|
self.empty_result = True
|
||||||
|
@ -147,9 +144,9 @@ class PolicyEngine:
|
||||||
if len(all_results) == 0:
|
if len(all_results) == 0:
|
||||||
return PolicyResult(self.empty_result)
|
return PolicyResult(self.empty_result)
|
||||||
passing = False
|
passing = False
|
||||||
if self.mode == PolicyEngineMode.MODE_AND:
|
if self.mode == PolicyEngineMode.MODE_ALL:
|
||||||
passing = all(x.passing for x in all_results)
|
passing = all(x.passing for x in all_results)
|
||||||
if self.mode == PolicyEngineMode.MODE_OR:
|
if self.mode == PolicyEngineMode.MODE_ANY:
|
||||||
passing = any(x.passing for x in all_results)
|
passing = any(x.passing for x in all_results)
|
||||||
result = PolicyResult(passing)
|
result = PolicyResult(passing)
|
||||||
result.messages = tuple(y for x in all_results for y in x.messages)
|
result.messages = tuple(y for x in all_results for y in x.messages)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Generated by Django 3.1.7 on 2021-03-31 08:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_policies", "0006_auto_20210329_1334"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
# Create field with default as all for backwards compat
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="policybindingmodel",
|
||||||
|
name="policy_engine_mode",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("all", "ALL, all policies must pass"),
|
||||||
|
("any", "ANY, any policy must pass"),
|
||||||
|
],
|
||||||
|
default="all",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
# Set default for new objects to any
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="policybindingmodel",
|
||||||
|
name="policy_engine_mode",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("all", "ALL, all policies must pass"),
|
||||||
|
("any", "ANY, any policy must pass"),
|
||||||
|
],
|
||||||
|
default="any",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -18,6 +18,15 @@ from authentik.policies.exceptions import PolicyException
|
||||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyEngineMode(models.TextChoices):
|
||||||
|
"""Decide how results of multiple policies should be combined."""
|
||||||
|
|
||||||
|
# pyright: reportGeneralTypeIssues=false
|
||||||
|
MODE_ALL = "all", _("ALL, all policies must pass") # type: "PolicyEngineMode"
|
||||||
|
# pyright: reportGeneralTypeIssues=false
|
||||||
|
MODE_ANY = "any", _("ANY, any policy must pass") # type: "PolicyEngineMode"
|
||||||
|
|
||||||
|
|
||||||
class PolicyBindingModel(models.Model):
|
class PolicyBindingModel(models.Model):
|
||||||
"""Base Model for objects that have policies applied to them."""
|
"""Base Model for objects that have policies applied to them."""
|
||||||
|
|
||||||
|
@ -27,6 +36,11 @@ class PolicyBindingModel(models.Model):
|
||||||
"Policy", through="PolicyBinding", related_name="bindings", blank=True
|
"Policy", through="PolicyBinding", related_name="bindings", blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
policy_engine_mode = models.TextField(
|
||||||
|
choices=PolicyEngineMode.choices,
|
||||||
|
default=PolicyEngineMode.MODE_ANY,
|
||||||
|
)
|
||||||
|
|
||||||
objects = InheritanceManager()
|
objects = InheritanceManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -4,9 +4,14 @@ from django.test import TestCase
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.policies.dummy.models import DummyPolicy
|
from authentik.policies.dummy.models import DummyPolicy
|
||||||
from authentik.policies.engine import PolicyEngine, PolicyEngineMode
|
from authentik.policies.engine import PolicyEngine
|
||||||
from authentik.policies.expression.models import ExpressionPolicy
|
from authentik.policies.expression.models import ExpressionPolicy
|
||||||
from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel
|
from authentik.policies.models import (
|
||||||
|
Policy,
|
||||||
|
PolicyBinding,
|
||||||
|
PolicyBindingModel,
|
||||||
|
PolicyEngineMode,
|
||||||
|
)
|
||||||
from authentik.policies.tests.test_process import clear_policy_cache
|
from authentik.policies.tests.test_process import clear_policy_cache
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,9 +49,11 @@ class TestPolicyEngine(TestCase):
|
||||||
self.assertEqual(result.passing, True)
|
self.assertEqual(result.passing, True)
|
||||||
self.assertEqual(result.messages, ("dummy",))
|
self.assertEqual(result.messages, ("dummy",))
|
||||||
|
|
||||||
def test_engine_mode_and(self):
|
def test_engine_mode_all(self):
|
||||||
"""Ensure all policies passes with AND mode (false and true -> false)"""
|
"""Ensure all policies passes with AND mode (false and true -> false)"""
|
||||||
pbm = PolicyBindingModel.objects.create()
|
pbm = PolicyBindingModel.objects.create(
|
||||||
|
policy_engine_mode=PolicyEngineMode.MODE_ALL
|
||||||
|
)
|
||||||
PolicyBinding.objects.create(target=pbm, policy=self.policy_false, order=0)
|
PolicyBinding.objects.create(target=pbm, policy=self.policy_false, order=0)
|
||||||
PolicyBinding.objects.create(target=pbm, policy=self.policy_true, order=1)
|
PolicyBinding.objects.create(target=pbm, policy=self.policy_true, order=1)
|
||||||
engine = PolicyEngine(pbm, self.user)
|
engine = PolicyEngine(pbm, self.user)
|
||||||
|
@ -60,13 +67,14 @@ class TestPolicyEngine(TestCase):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_engine_mode_or(self):
|
def test_engine_mode_any(self):
|
||||||
"""Ensure all policies passes with OR mode (false and true -> true)"""
|
"""Ensure all policies passes with OR mode (false and true -> true)"""
|
||||||
pbm = PolicyBindingModel.objects.create()
|
pbm = PolicyBindingModel.objects.create(
|
||||||
|
policy_engine_mode=PolicyEngineMode.MODE_ANY
|
||||||
|
)
|
||||||
PolicyBinding.objects.create(target=pbm, policy=self.policy_false, order=0)
|
PolicyBinding.objects.create(target=pbm, policy=self.policy_false, order=0)
|
||||||
PolicyBinding.objects.create(target=pbm, policy=self.policy_true, order=1)
|
PolicyBinding.objects.create(target=pbm, policy=self.policy_true, order=1)
|
||||||
engine = PolicyEngine(pbm, self.user)
|
engine = PolicyEngine(pbm, self.user)
|
||||||
engine.mode = PolicyEngineMode.MODE_OR
|
|
||||||
result = engine.build().result
|
result = engine.build().result
|
||||||
self.assertEqual(result.passing, True)
|
self.assertEqual(result.passing, True)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
@ -26,6 +26,7 @@ class LDAPSourceForm(forms.ModelForm):
|
||||||
"name",
|
"name",
|
||||||
"slug",
|
"slug",
|
||||||
"enabled",
|
"enabled",
|
||||||
|
"policy_engine_mode",
|
||||||
# -- start of our custom fields
|
# -- start of our custom fields
|
||||||
"server_uri",
|
"server_uri",
|
||||||
"start_tls",
|
"start_tls",
|
||||||
|
|
|
@ -32,6 +32,7 @@ class OAuthSourceForm(forms.ModelForm):
|
||||||
"name",
|
"name",
|
||||||
"slug",
|
"slug",
|
||||||
"enabled",
|
"enabled",
|
||||||
|
"policy_engine_mode",
|
||||||
"authentication_flow",
|
"authentication_flow",
|
||||||
"enrollment_flow",
|
"enrollment_flow",
|
||||||
"provider_type",
|
"provider_type",
|
||||||
|
|
|
@ -35,6 +35,7 @@ class SAMLSourceForm(forms.ModelForm):
|
||||||
"name",
|
"name",
|
||||||
"slug",
|
"slug",
|
||||||
"enabled",
|
"enabled",
|
||||||
|
"policy_engine_mode",
|
||||||
"pre_authentication_flow",
|
"pre_authentication_flow",
|
||||||
"authentication_flow",
|
"authentication_flow",
|
||||||
"enrollment_flow",
|
"enrollment_flow",
|
||||||
|
|
66
swagger.yaml
66
swagger.yaml
|
@ -3421,6 +3421,11 @@ paths:
|
||||||
description: ''
|
description: ''
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
- name: policy_engine_mode
|
||||||
|
in: query
|
||||||
|
description: ''
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
- name: fsb_uuid
|
- name: fsb_uuid
|
||||||
in: query
|
in: query
|
||||||
description: ''
|
description: ''
|
||||||
|
@ -14729,6 +14734,12 @@ definitions:
|
||||||
meta_publisher:
|
meta_publisher:
|
||||||
title: Meta publisher
|
title: Meta publisher
|
||||||
type: string
|
type: string
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
Group:
|
Group:
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
|
@ -15288,6 +15299,12 @@ definitions:
|
||||||
title: Cache count
|
title: Cache count
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
Stage:
|
Stage:
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
|
@ -15358,13 +15375,12 @@ definitions:
|
||||||
type: integer
|
type: integer
|
||||||
maximum: 2147483647
|
maximum: 2147483647
|
||||||
minimum: -2147483648
|
minimum: -2147483648
|
||||||
policies:
|
policy_engine_mode:
|
||||||
type: array
|
title: Policy engine mode
|
||||||
items:
|
type: string
|
||||||
type: string
|
enum:
|
||||||
format: uuid
|
- all
|
||||||
readOnly: true
|
- any
|
||||||
uniqueItems: true
|
|
||||||
ErrorDetail:
|
ErrorDetail:
|
||||||
required:
|
required:
|
||||||
- string
|
- string
|
||||||
|
@ -16151,6 +16167,12 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
name:
|
name:
|
||||||
title: Name
|
title: Name
|
||||||
description: Source's display Name.
|
description: Source's display Name.
|
||||||
|
@ -17172,6 +17194,12 @@ definitions:
|
||||||
title: Verbose name plural
|
title: Verbose name plural
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
UserSetting:
|
UserSetting:
|
||||||
required:
|
required:
|
||||||
- object_uid
|
- object_uid
|
||||||
|
@ -17246,6 +17274,12 @@ definitions:
|
||||||
title: Verbose name plural
|
title: Verbose name plural
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
server_uri:
|
server_uri:
|
||||||
title: Server URI
|
title: Server URI
|
||||||
type: string
|
type: string
|
||||||
|
@ -17388,6 +17422,12 @@ definitions:
|
||||||
title: Verbose name plural
|
title: Verbose name plural
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
provider_type:
|
provider_type:
|
||||||
title: Provider type
|
title: Provider type
|
||||||
type: string
|
type: string
|
||||||
|
@ -17504,6 +17544,12 @@ definitions:
|
||||||
title: Verbose name plural
|
title: Verbose name plural
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
pre_authentication_flow:
|
pre_authentication_flow:
|
||||||
title: Pre authentication flow
|
title: Pre authentication flow
|
||||||
description: Flow used before authentication.
|
description: Flow used before authentication.
|
||||||
|
@ -18196,6 +18242,12 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
policy_engine_mode:
|
||||||
|
title: Policy engine mode
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- any
|
||||||
name:
|
name:
|
||||||
title: Name
|
title: Name
|
||||||
description: Source's display Name.
|
description: Source's display Name.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CoreApi, Application, ProvidersApi, Provider } from "authentik-api";
|
import { CoreApi, Application, ProvidersApi, Provider, ApplicationPolicyEngineModeEnum } from "authentik-api";
|
||||||
import { gettext } from "django";
|
import { gettext } from "django";
|
||||||
import { customElement, property } from "lit-element";
|
import { customElement, property } from "lit-element";
|
||||||
import { html, TemplateResult } from "lit-html";
|
import { html, TemplateResult } from "lit-html";
|
||||||
|
@ -97,6 +97,19 @@ export class ApplicationForm extends Form<Application> {
|
||||||
}), html``)}
|
}), html``)}
|
||||||
</select>
|
</select>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${gettext("Policy engine mode")}
|
||||||
|
?required=${true}
|
||||||
|
name="policyEngineMode">
|
||||||
|
<select class="pf-c-form-control">
|
||||||
|
<option value=${ApplicationPolicyEngineModeEnum.Any} ?selected=${this.application?.policyEngineMode === ApplicationPolicyEngineModeEnum.Any}>
|
||||||
|
${gettext("ANY, any policy must match to grant access.")}
|
||||||
|
</option>
|
||||||
|
<option value=${ApplicationPolicyEngineModeEnum.All} ?selected=${this.application?.policyEngineMode === ApplicationPolicyEngineModeEnum.All}>
|
||||||
|
${gettext("ALL, all policies must match to grant access.")}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${gettext("Launch URL")}
|
label=${gettext("Launch URL")}
|
||||||
name="launchUrl">
|
name="launchUrl">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Flow, FlowDesignationEnum, FlowsApi } from "authentik-api";
|
import { Flow, FlowDesignationEnum, FlowPolicyEngineModeEnum, FlowsApi } from "authentik-api";
|
||||||
import { gettext } from "django";
|
import { gettext } from "django";
|
||||||
import { customElement, property } from "lit-element";
|
import { customElement, property } from "lit-element";
|
||||||
import { html, TemplateResult } from "lit-html";
|
import { html, TemplateResult } from "lit-html";
|
||||||
|
@ -93,6 +93,19 @@ export class FlowForm extends Form<Flow> {
|
||||||
<input type="text" value="${ifDefined(this.flow?.slug)}" class="pf-c-form-control" required>
|
<input type="text" value="${ifDefined(this.flow?.slug)}" class="pf-c-form-control" required>
|
||||||
<p class="pf-c-form__helper-text">${gettext("Visible in the URL.")}</p>
|
<p class="pf-c-form__helper-text">${gettext("Visible in the URL.")}</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${gettext("Policy engine mode")}
|
||||||
|
?required=${true}
|
||||||
|
name="policyEngineMode">
|
||||||
|
<select class="pf-c-form-control">
|
||||||
|
<option value=${FlowPolicyEngineModeEnum.Any} ?selected=${this.flow?.policyEngineMode === FlowPolicyEngineModeEnum.Any}>
|
||||||
|
${gettext("ANY, any policy must match to grant access.")}
|
||||||
|
</option>
|
||||||
|
<option value=${FlowPolicyEngineModeEnum.All} ?selected=${this.flow?.policyEngineMode === FlowPolicyEngineModeEnum.All}>
|
||||||
|
${gettext("ALL, all policies must match to grant access.")}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${gettext("Designation")}
|
label=${gettext("Designation")}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
|
|
|
@ -2,7 +2,21 @@
|
||||||
title: Next
|
title: Next
|
||||||
---
|
---
|
||||||
|
|
||||||
# TBD
|
## Headline Changes
|
||||||
|
|
||||||
|
- Configurable Policy engine mode
|
||||||
|
|
||||||
|
In the past, all objects, which could have policies attached to them, required *all* policies to pass to consider an action successful.
|
||||||
|
You can now configure if *all* policies need to pass, or if *any* policy needs to pass.
|
||||||
|
|
||||||
|
This can now be configured for the following objects:
|
||||||
|
|
||||||
|
- Applications (access restrictions)
|
||||||
|
- Sources
|
||||||
|
- Flows
|
||||||
|
- Flow-stage bindings
|
||||||
|
|
||||||
|
For backwards compatibility, this is set to *all*, but new objects will default to *any*.
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
|
|
||||||
|
|
Reference in a new issue