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 argparse import ArgumentParser
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
|
import pytest
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test.runner import DiscoverRunner
|
from django.test.runner import DiscoverRunner
|
||||||
|
|
||||||
@ -105,6 +106,4 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
|
|||||||
f"path instead."
|
f"path instead."
|
||||||
)
|
)
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
return pytest.main(self.args)
|
return pytest.main(self.args)
|
||||||
|
@ -133,13 +133,14 @@ class TestUserLoginStage(FlowTestCase):
|
|||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||||
data={"remember_me": True},
|
data={"remember_me": True},
|
||||||
)
|
)
|
||||||
|
_now = now().timestamp()
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
self.assertNotEqual(list(self.client.session.keys()), [])
|
self.assertNotEqual(list(self.client.session.keys()), [])
|
||||||
session_key = self.client.session.session_key
|
session_key = self.client.session.session_key
|
||||||
session = AuthenticatedSession.objects.filter(session_key=session_key).first()
|
session = AuthenticatedSession.objects.filter(session_key=session_key).first()
|
||||||
self.assertAlmostEqual(
|
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.session_duration).total_seconds()
|
||||||
+ timedelta_from_string(self.stage.remember_me_offset).total_seconds(),
|
+ timedelta_from_string(self.stage.remember_me_offset).total_seconds(),
|
||||||
delta=1,
|
delta=1,
|
||||||
|
@ -26,6 +26,9 @@ entries:
|
|||||||
!Find [authentik_flows.flow, [slug, default-user-settings-flow]]
|
!Find [authentik_flows.flow, [slug, default-user-settings-flow]]
|
||||||
identifiers:
|
identifiers:
|
||||||
domain: authentik-default
|
domain: authentik-default
|
||||||
default: True
|
default: true
|
||||||
state: created
|
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
|
model: authentik_brands.brand
|
||||||
|
@ -76,7 +76,7 @@ show_missing = true
|
|||||||
DJANGO_SETTINGS_MODULE = "authentik.root.settings"
|
DJANGO_SETTINGS_MODULE = "authentik.root.settings"
|
||||||
python_files = ["tests.py", "test_*.py", "*_tests.py"]
|
python_files = ["tests.py", "test_*.py", "*_tests.py"]
|
||||||
junit_family = "xunit2"
|
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 = [
|
filterwarnings = [
|
||||||
"ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning",
|
"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",
|
"ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning",
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
#### `!KeyOf`
|
#### `!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_.
|
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`
|
#### `!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.
|
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:
|
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 [
|
|
||||||
|
```yaml
|
||||||
|
configure_flow:
|
||||||
|
!Find [
|
||||||
authentik_flows.flow,
|
authentik_flows.flow,
|
||||||
[
|
[!Context property_name, !Context property_value],
|
||||||
!Context property_name,
|
|
||||||
!Context property_value
|
|
||||||
]
|
]
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Looks up any model and resolves to the the matches' primary key.
|
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`
|
#### `!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]`.
|
Find values from the context. Can optionally be called with a default like `!Context [foo, default-value]`.
|
||||||
|
|
||||||
#### `!Format`
|
#### `!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.
|
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:
|
Full example:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
attributes: !If [
|
attributes: !If [
|
||||||
!Condition [...], # Or any valid YAML or custom tag. Evaluated as boolean in Python
|
!Condition [...], # Or any valid YAML or custom tag. Evaluated as boolean in Python
|
||||||
{ # When condition evaluates to true
|
{
|
||||||
|
# When condition evaluates to true
|
||||||
dictionary:
|
dictionary:
|
||||||
{
|
{
|
||||||
with:
|
with: { keys: "and_values" },
|
||||||
{
|
and_nested_custom_tags: !Format ["foo-%s", !Context foo],
|
||||||
keys: "and_values"
|
|
||||||
},
|
},
|
||||||
and_nested_custom_tags: !Format ["foo-%s", !Context foo]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[ # When condition evaluates to false
|
[
|
||||||
|
# When condition evaluates to false
|
||||||
list,
|
list,
|
||||||
with,
|
with,
|
||||||
items,
|
items,
|
||||||
!Format ["foo-%s", !Context foo]
|
!Format ["foo-%s", !Context foo],
|
||||||
|
],
|
||||||
]
|
]
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Conditionally add YAML to a blueprint.
|
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:
|
Minimal example:
|
||||||
|
|
||||||
`required: !Condition [OR, true]`
|
```yaml
|
||||||
|
required: !Condition [OR, true]
|
||||||
|
```
|
||||||
|
|
||||||
Full example:
|
Full example:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
required: !Condition [
|
required: !Condition [
|
||||||
AND, # Valid modes are: AND, NAND, OR, NOR, XOR, XNOR
|
AND, # Valid modes are: AND, NAND, OR, NOR, XOR, XNOR
|
||||||
!Context instance_name,
|
!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:
|
This tag takes 3 arguments:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
!Enumerate [<iterable>, <output_object_type>, <single_item_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:
|
This tag takes 1 argument:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
!Index <depth>
|
!Index <depth>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -158,7 +175,7 @@ This tag is only valid inside an `!Enumerate` tag
|
|||||||
|
|
||||||
This tag takes 1 argument:
|
This tag takes 1 argument:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
!Value <depth>
|
!Value <depth>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -170,38 +187,51 @@ For example, given a sequence like this - `["a", "b", "c"]`, this tag will resol
|
|||||||
|
|
||||||
Minimal examples:
|
Minimal examples:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
configuration_stages: !Enumerate [
|
configuration_stages: !Enumerate [
|
||||||
!Context map_of_totp_stage_names_and_types,
|
!Context map_of_totp_stage_names_and_types,
|
||||||
SEQ, # Output a sequence
|
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
|
!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:
|
The above example will resolve to something like this:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
configuration_stages:
|
configuration_stages:
|
||||||
- !Find [authentik_stages_authenticator_<stage_type_1>.authenticator<stage_type_1>stage, [name, <stage_name_1>]]
|
- !Find [
|
||||||
- !Find [authentik_stages_authenticator_<stage_type_2>.authenticator<stage_type_2>stage, [name, <stage_name_2>]]
|
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:
|
Similarly, a mapping can be generated like so:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
example: !Enumerate [
|
example: !Enumerate [
|
||||||
!Context list_of_totp_stage_names,
|
!Context list_of_totp_stage_names,
|
||||||
MAP, # Output a map
|
MAP, # Output a map
|
||||||
[
|
[
|
||||||
!Index 0, # The key to assign to each entry
|
!Index 0, # The key to assign to each entry
|
||||||
!Value 0, # The value to assign to each entry
|
!Value 0, # The value to assign to each entry
|
||||||
|
],
|
||||||
]
|
]
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The above example will resolve to something like this:
|
The above example will resolve to something like this:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
example:
|
example:
|
||||||
0: <stage_name_1>
|
0: <stage_name_1>
|
||||||
1: <stage_name_2>
|
1: <stage_name_2>
|
||||||
@ -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.
|
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 [
|
example: !Enumerate [
|
||||||
!Context sequence, # ["foo", "bar"]
|
!Context sequence, # ["foo", "bar"]
|
||||||
MAP, # Output a map
|
MAP, # Output a map
|
||||||
[
|
[
|
||||||
!Index 0, # Use the indexes of the items in the sequence as keys
|
!Index 0, # Use the indexes of the items in the sequence as keys
|
||||||
!Enumerate [ # Nested enumeration
|
!Enumerate [
|
||||||
|
# Nested enumeration
|
||||||
# Iterate over each item of the parent enumerate tag.
|
# Iterate over each item of the parent enumerate tag.
|
||||||
# Notice depth is 1, not 0, since we are inside the nested enumeration tag!
|
# Notice depth is 1, not 0, since we are inside the nested enumeration tag!
|
||||||
!Value 1,
|
!Value 1,
|
||||||
SEQ, # Output a sequence
|
SEQ, # Output a sequence
|
||||||
!Format ["%s: (index: %d, letter: %s)", !Value 1, !Index 0, !Value 0]
|
!Format [
|
||||||
|
"%s: (index: %d, letter: %s)",
|
||||||
|
!Value 1,
|
||||||
|
!Index 0,
|
||||||
|
!Value 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
]
|
]
|
||||||
]
|
|
||||||
]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The above example will resolve to something like this:
|
The above example will resolve to something like this:
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
'0':
|
"0":
|
||||||
- 'foo: (index: 0, letter: f)'
|
- "foo: (index: 0, letter: f)"
|
||||||
- 'foo: (index: 1, letter: o)'
|
- "foo: (index: 1, letter: o)"
|
||||||
- 'foo: (index: 2, letter: o)'
|
- "foo: (index: 2, letter: o)"
|
||||||
'1':
|
"1":
|
||||||
- 'bar: (index: 0, letter: b)'
|
- "bar: (index: 0, letter: b)"
|
||||||
- 'bar: (index: 1, letter: a)'
|
- "bar: (index: 1, letter: a)"
|
||||||
- 'bar: (index: 2, letter: r)'
|
- "bar: (index: 2, letter: r)"
|
||||||
```
|
```
|
||||||
|
Reference in New Issue
Block a user