diff --git a/authentik/policies/api/bindings.py b/authentik/policies/api/bindings.py index 38041b14c..59b28d262 100644 --- a/authentik/policies/api/bindings.py +++ b/authentik/policies/api/bindings.py @@ -77,6 +77,7 @@ class PolicyBindingSerializer(ModelSerializer): "enabled", "order", "timeout", + "failure_result", ] def validate(self, attrs: OrderedDict) -> OrderedDict: diff --git a/authentik/policies/migrations/0011_policybinding_failure_result_and_more.py b/authentik/policies/migrations/0011_policybinding_failure_result_and_more.py new file mode 100644 index 000000000..538cdc2e6 --- /dev/null +++ b/authentik/policies/migrations/0011_policybinding_failure_result_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.5 on 2023-09-13 18:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_policies", "0010_alter_policy_name"), + ] + + operations = [ + migrations.AddField( + model_name="policybinding", + name="failure_result", + field=models.BooleanField( + default=False, help_text="Result if the Policy execution fails." + ), + ), + migrations.AlterField( + model_name="policybinding", + name="timeout", + field=models.PositiveIntegerField( + default=30, help_text="Timeout after which Policy execution is terminated." + ), + ), + ] diff --git a/authentik/policies/models.py b/authentik/policies/models.py index 0eb353206..2e7d69c61 100644 --- a/authentik/policies/models.py +++ b/authentik/policies/models.py @@ -85,9 +85,12 @@ class PolicyBinding(SerializerModel): default=False, help_text=_("Negates the outcome of the policy. Messages are unaffected."), ) - timeout = models.IntegerField( + timeout = models.PositiveIntegerField( default=30, help_text=_("Timeout after which Policy execution is terminated.") ) + failure_result = models.BooleanField( + default=False, help_text=_("Result if the Policy execution fails.") + ) order = models.IntegerField() diff --git a/authentik/policies/process.py b/authentik/policies/process.py index 3c6710a51..89dc548da 100644 --- a/authentik/policies/process.py +++ b/authentik/policies/process.py @@ -98,8 +98,8 @@ class PolicyProcess(PROCESS_CLASS): # Create policy exception event, only when we're not debugging if not self.request.debug: self.create_event(EventAction.POLICY_EXCEPTION, message=error_string) - LOGGER.debug("P_ENG(proc): error", exc=src_exc) - policy_result = PolicyResult(False, str(src_exc)) + LOGGER.debug("P_ENG(proc): error, using failure result", exc=src_exc) + policy_result = PolicyResult(self.binding.failure_result, str(src_exc)) policy_result.source_binding = self.binding should_cache = self.request.should_cache if should_cache: diff --git a/authentik/policies/tests/test_engine.py b/authentik/policies/tests/test_engine.py index 1159e2db3..d173e40ac 100644 --- a/authentik/policies/tests/test_engine.py +++ b/authentik/policies/tests/test_engine.py @@ -97,6 +97,17 @@ class TestPolicyEngine(TestCase): self.assertEqual(result.passing, False) self.assertEqual(result.messages, ("division by zero",)) + def test_engine_policy_error_failure(self): + """Test policy raising an error flag""" + pbm = PolicyBindingModel.objects.create() + PolicyBinding.objects.create( + target=pbm, policy=self.policy_raises, order=0, failure_result=True + ) + engine = PolicyEngine(pbm, self.user) + result = engine.build().result + self.assertEqual(result.passing, True) + self.assertEqual(result.messages, ("division by zero",)) + def test_engine_policy_type(self): """Test invalid policy type""" pbm = PolicyBindingModel.objects.create() diff --git a/blueprints/schema.json b/blueprints/schema.json index 0c925724c..91a6891b1 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -3650,10 +3650,15 @@ }, "timeout": { "type": "integer", - "minimum": -2147483648, + "minimum": 0, "maximum": 2147483647, "title": "Timeout", "description": "Timeout after which Policy execution is terminated." + }, + "failure_result": { + "type": "boolean", + "title": "Failure result", + "description": "Result if the Policy execution fails." } }, "required": [] diff --git a/schema.yml b/schema.yml index c97ab5d21..351823850 100644 --- a/schema.yml +++ b/schema.yml @@ -35954,8 +35954,11 @@ components: timeout: type: integer maximum: 2147483647 - minimum: -2147483648 + minimum: 0 description: Timeout after which Policy execution is terminated. + failure_result: + type: boolean + description: Result if the Policy execution fails. PatchedPromptRequest: type: object description: Prompt Serializer @@ -37046,8 +37049,11 @@ components: timeout: type: integer maximum: 2147483647 - minimum: -2147483648 + minimum: 0 description: Timeout after which Policy execution is terminated. + failure_result: + type: boolean + description: Result if the Policy execution fails. required: - group_obj - order @@ -37085,8 +37091,11 @@ components: timeout: type: integer maximum: 2147483647 - minimum: -2147483648 + minimum: 0 description: Timeout after which Policy execution is terminated. + failure_result: + type: boolean + description: Result if the Policy execution fails. required: - order - target diff --git a/web/src/admin/policies/PolicyBindingForm.ts b/web/src/admin/policies/PolicyBindingForm.ts index 458210edf..7de9d66f0 100644 --- a/web/src/admin/policies/PolicyBindingForm.ts +++ b/web/src/admin/policies/PolicyBindingForm.ts @@ -3,6 +3,7 @@ import { first, groupBy } from "@goauthentik/common/utils"; import "@goauthentik/components/ak-toggle-group"; import "@goauthentik/elements/forms/HorizontalFormElement"; import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; +import "@goauthentik/elements/forms/Radio"; import "@goauthentik/elements/forms/SearchSelect"; import { msg } from "@lit/localize"; @@ -298,6 +299,26 @@ export class PolicyBindingForm extends ModelForm { required /> + + + +

+ ${msg("Result used when policy execution fails.")} +

+
`; } }