blueprints: only create default brand if no other default brand exists (#9222)

* blueprints: only create default brand if no other default brand exists

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix invalid blueprint

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix flaky test, improve pytest output

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L
2024-04-12 14:59:48 +02:00
committed by GitHub
parent e03c25a600
commit 7ef14eb86d
6 changed files with 140 additions and 80 deletions

View File

@ -0,0 +1,21 @@
from os import environ
import pytest
from authentik import get_full_version
IS_CI = "CI" in environ
@pytest.hookimpl(hookwrapper=True)
def pytest_sessionstart(*_, **__):
"""Clear the console ahead of the pytest output starting"""
if not IS_CI:
print("\x1b[2J\x1b[H")
yield
@pytest.hookimpl(trylast=True)
def pytest_report_header(*_, **__):
"""Add authentik version to pytest output"""
return [f"authentik version: {get_full_version()}"]

View File

@ -4,6 +4,7 @@ import os
from argparse import ArgumentParser
from unittest import TestCase
import pytest
from django.conf import settings
from django.test.runner import DiscoverRunner
@ -105,6 +106,4 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
f"path instead."
)
import pytest
return pytest.main(self.args)

View File

@ -133,13 +133,14 @@ class TestUserLoginStage(FlowTestCase):
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"remember_me": True},
)
_now = now().timestamp()
self.assertEqual(response.status_code, 200)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
self.assertNotEqual(list(self.client.session.keys()), [])
session_key = self.client.session.session_key
session = AuthenticatedSession.objects.filter(session_key=session_key).first()
self.assertAlmostEqual(
session.expires.timestamp() - now().timestamp(),
session.expires.timestamp() - _now,
timedelta_from_string(self.stage.session_duration).total_seconds()
+ timedelta_from_string(self.stage.remember_me_offset).total_seconds(),
delta=1,

View File

@ -26,6 +26,9 @@ entries:
!Find [authentik_flows.flow, [slug, default-user-settings-flow]]
identifiers:
domain: authentik-default
default: True
default: true
state: created
conditions:
# Only create default brand if no other default brand exists
- !Condition [NOR, !Find [authentik_brands.brand, [default, True]]]
model: authentik_brands.brand

View File

@ -76,7 +76,7 @@ show_missing = true
DJANGO_SETTINGS_MODULE = "authentik.root.settings"
python_files = ["tests.py", "test_*.py", "*_tests.py"]
junit_family = "xunit2"
addopts = "-p no:celery --junitxml=unittest.xml -vv --full-trace --doctest-modules"
addopts = "-p no:celery -p authentik.root.test_plugin --junitxml=unittest.xml -vv --full-trace --doctest-modules"
filterwarnings = [
"ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning",
"ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning",

View File

@ -2,7 +2,11 @@
#### `!KeyOf`
Example: `policy: !KeyOf my-policy-id`
Example:
```yaml
policy: !KeyOf my-policy-id
```
Resolves to the primary key of the model instance defined by id _my-policy-id_.
@ -10,7 +14,11 @@ If no matching entry can be found, an error is raised and the blueprint is inval
#### `!Env`
Example: `password: !Env my_env_var`
Example:
```yaml
password: !Env my_env_var
```
Returns the value of the given environment variable. Can be used as a scalar with `!Env my_env_var, default` to return a default value.
@ -18,16 +26,16 @@ Returns the value of the given environment variable. Can be used as a scalar wit
Examples:
`configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]`
```yaml
configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]
```
configure_flow: !Find [
authentik_flows.flow,
[
!Context property_name,
!Context property_value
]
]
```yaml
configure_flow:
!Find [
authentik_flows.flow,
[!Context property_name, !Context property_value],
]
```
Looks up any model and resolves to the the matches' primary key.
@ -35,13 +43,21 @@ First argument is the model to be queried, remaining arguments are expected to b
#### `!Context`
Example: `configure_flow: !Context foo`
Example:
```yaml
configure_flow: !Context foo
```
Find values from the context. Can optionally be called with a default like `!Context [foo, default-value]`.
#### `!Format`
Example: `name: !Format [my-policy-%s, !Context instance_name]`
Example:
```yaml
name: !Format [my-policy-%s, !Context instance_name]
```
Format a string using python's % formatting. First argument is the format string, any remaining arguments are used for formatting.
@ -63,26 +79,25 @@ required: !If [true, true, false]
Full example:
```
```yaml
attributes: !If [
!Condition [...], # Or any valid YAML or custom tag. Evaluated as boolean in Python
{ # When condition evaluates to true
dictionary:
!Condition [...], # Or any valid YAML or custom tag. Evaluated as boolean in Python
{
with:
{
keys: "and_values"
},
and_nested_custom_tags: !Format ["foo-%s", !Context foo]
}
},
[ # When condition evaluates to false
list,
with,
items,
!Format ["foo-%s", !Context foo]
# When condition evaluates to true
dictionary:
{
with: { keys: "and_values" },
and_nested_custom_tags: !Format ["foo-%s", !Context foo],
},
},
[
# When condition evaluates to false
list,
with,
items,
!Format ["foo-%s", !Context foo],
],
]
]
```
Conditionally add YAML to a blueprint.
@ -95,11 +110,13 @@ The second argument is used when the condition is `true`, and the third - when `
Minimal example:
`required: !Condition [OR, true]`
```yaml
required: !Condition [OR, true]
```
Full example:
```
```yaml
required: !Condition [
AND, # Valid modes are: AND, NAND, OR, NOR, XOR, XNOR
!Context instance_name,
@ -124,7 +141,7 @@ These tags collectively make it possible to iterate over objects which support i
This tag takes 3 arguments:
```
```yaml
!Enumerate [<iterable>, <output_object_type>, <single_item_yaml>]
```
@ -140,7 +157,7 @@ This tag is only valid inside an `!Enumerate` tag
This tag takes 1 argument:
```
```yaml
!Index <depth>
```
@ -158,7 +175,7 @@ This tag is only valid inside an `!Enumerate` tag
This tag takes 1 argument:
```
```yaml
!Value <depth>
```
@ -170,41 +187,54 @@ For example, given a sequence like this - `["a", "b", "c"]`, this tag will resol
Minimal examples:
```
```yaml
configuration_stages: !Enumerate [
!Context map_of_totp_stage_names_and_types,
SEQ, # Output a sequence
!Find [!Format ["authentik_stages_authenticator_%s.authenticator%sstage", !Index 0, !Index 0], [name, !Value 0]] # The value of each item in the sequence
]
!Context map_of_totp_stage_names_and_types,
SEQ, # Output a sequence
!Find [
!Format [
"authentik_stages_authenticator_%s.authenticator%sstage",
!Index 0,
!Index 0,
],
[name, !Value 0],
], # The value of each item in the sequence
]
```
The above example will resolve to something like this:
```
```yaml
configuration_stages:
- !Find [authentik_stages_authenticator_<stage_type_1>.authenticator<stage_type_1>stage, [name, <stage_name_1>]]
- !Find [authentik_stages_authenticator_<stage_type_2>.authenticator<stage_type_2>stage, [name, <stage_name_2>]]
- !Find [
authentik_stages_authenticator_<stage_type_1>.authenticator<stage_type_1>stage,
[name, <stage_name_1>],
]
- !Find [
authentik_stages_authenticator_<stage_type_2>.authenticator<stage_type_2>stage,
[name, <stage_name_2>],
]
```
Similarly, a mapping can be generated like so:
```
```yaml
example: !Enumerate [
!Context list_of_totp_stage_names,
MAP, # Output a map
[
!Index 0, # The key to assign to each entry
!Value 0, # The value to assign to each entry
!Context list_of_totp_stage_names,
MAP, # Output a map
[
!Index 0, # The key to assign to each entry
!Value 0, # The value to assign to each entry
],
]
]
```
The above example will resolve to something like this:
```
```yaml
example:
0: <stage_name_1>
1: <stage_name_2>
0: <stage_name_1>
1: <stage_name_2>
```
Full example:
@ -213,32 +243,38 @@ Full example:
Note that an `!Enumeration` tag's iterable can never be an `!Item` or `!Value` tag with a depth of `0`. Minimum depth allowed is `1`. This is because a depth of `0` refers to the `!Enumeration` tag the `!Item` or `!Value` tag is in, and an `!Enumeration` tag cannot iterate over itself.
:::
```
```yaml
example: !Enumerate [
!Context sequence, # ["foo", "bar"]
MAP, # Output a map
[
!Index 0, # Use the indexes of the items in the sequence as keys
!Enumerate [ # Nested enumeration
# Iterate over each item of the parent enumerate tag.
# Notice depth is 1, not 0, since we are inside the nested enumeration tag!
!Value 1,
SEQ, # Output a sequence
!Format ["%s: (index: %d, letter: %s)", !Value 1, !Index 0, !Value 0]
]
!Context sequence, # ["foo", "bar"]
MAP, # Output a map
[
!Index 0, # Use the indexes of the items in the sequence as keys
!Enumerate [
# Nested enumeration
# Iterate over each item of the parent enumerate tag.
# Notice depth is 1, not 0, since we are inside the nested enumeration tag!
!Value 1,
SEQ, # Output a sequence
!Format [
"%s: (index: %d, letter: %s)",
!Value 1,
!Index 0,
!Value 0,
],
],
],
]
]
```
The above example will resolve to something like this:
```
'0':
- 'foo: (index: 0, letter: f)'
- 'foo: (index: 1, letter: o)'
- 'foo: (index: 2, letter: o)'
'1':
- 'bar: (index: 0, letter: b)'
- 'bar: (index: 1, letter: a)'
- 'bar: (index: 2, letter: r)'
```yaml
"0":
- "foo: (index: 0, letter: f)"
- "foo: (index: 1, letter: o)"
- "foo: (index: 2, letter: o)"
"1":
- "bar: (index: 0, letter: b)"
- "bar: (index: 1, letter: a)"
- "bar: (index: 2, letter: r)"
```