Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions authentik/root/test_plugin.py
Original file line number Diff line number Diff line change
@@ -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")

Check warning on line 14 in authentik/root/test_plugin.py

View check run for this annotation

Codecov / codecov/patch

authentik/root/test_plugin.py#L14

Added line #L14 was not covered by tests
yield


@pytest.hookimpl(trylast=True)
def pytest_report_header(*_, **__):
"""Add authentik version to pytest output"""
return [f"authentik version: {get_full_version()}"]
3 changes: 1 addition & 2 deletions authentik/root/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from argparse import ArgumentParser
from unittest import TestCase

import pytest
from django.conf import settings
from django.test.runner import DiscoverRunner

Expand Down Expand Up @@ -105,6 +106,4 @@ def run_tests(self, test_labels, extra_tests=None, **kwargs):
f"path instead."
)

import pytest

return pytest.main(self.args)
3 changes: 2 additions & 1 deletion authentik/stages/user_login/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,14 @@ def test_expiry_remember(self):
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,
Expand Down
5 changes: 4 additions & 1 deletion blueprints/default/default-brand.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
186 changes: 111 additions & 75 deletions website/developer-docs/blueprints/v1/tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,62 @@

#### `!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_.

If no matching entry can be found, an error is raised and the blueprint is invalid.

#### `!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.

#### `!Find`

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.
First argument is the model to be queried, remaining arguments are expected to be pairs of key=value pairs to query for.

#### `!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.

Expand All @@ -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.
Expand All @@ -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,
Expand All @@ -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>]
```

Expand All @@ -140,7 +157,7 @@ This tag is only valid inside an `!Enumerate` tag

This tag takes 1 argument:

```
```yaml
!Index <depth>
```

Expand All @@ -158,7 +175,7 @@ This tag is only valid inside an `!Enumerate` tag

This tag takes 1 argument:

```
```yaml
!Value <depth>
```

Expand All @@ -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:
Expand All @@ -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)"
```
Loading