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:
21
authentik/root/test_plugin.py
Normal file
21
authentik/root/test_plugin.py
Normal 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()}"]
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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)"
|
||||
```
|
||||
|
Reference in New Issue
Block a user