Compare commits
	
		
			27 Commits
		
	
	
		
			version/20
			...
			version/20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2a4daa5360 | |||
| e1a6dede54 | |||
| 17ee076f3d | |||
| 4d12a98c5d | |||
| 3a13d19695 | |||
| ed7bef9dbf | |||
| 4a17795df9 | |||
| 07b1aea767 | |||
| ab0f8d027d | |||
| b9fdb63a57 | |||
| 94833dd1e7 | |||
| 5262d89505 | |||
| ab3d47c437 | |||
| 14cd52686d | |||
| 1a39754fe9 | |||
| 8599eba863 | |||
| 4c6d21820e | |||
| ddee1c9a8c | |||
| 84678c41a8 | |||
| 7e1059dd43 | |||
| bc56ea6822 | |||
| 768dc55a71 | |||
| a0719ca65e | |||
| 38c8555f36 | |||
| 5b8223808e | |||
| 14f341f504 | |||
| c30aa90888 | 
| @ -1,5 +1,5 @@ | |||||||
| [bumpversion] | [bumpversion] | ||||||
| current_version = 2022.11.0 | current_version = 2022.11.2 | ||||||
| tag = True | tag = True | ||||||
| commit = True | commit = True | ||||||
| parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+) | parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+) | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @ -20,7 +20,12 @@ | |||||||
|     "todo-tree.tree.showCountsInTree": true, |     "todo-tree.tree.showCountsInTree": true, | ||||||
|     "todo-tree.tree.showBadges": true, |     "todo-tree.tree.showBadges": true, | ||||||
|     "python.formatting.provider": "black", |     "python.formatting.provider": "black", | ||||||
|     "yaml.customTags": ["!Find sequence", "!KeyOf scalar"], |     "yaml.customTags": [ | ||||||
|  |         "!Find sequence", | ||||||
|  |         "!KeyOf scalar", | ||||||
|  |         "!Context scalar", | ||||||
|  |         "!Format sequence" | ||||||
|  |     ], | ||||||
|     "typescript.preferences.importModuleSpecifier": "non-relative", |     "typescript.preferences.importModuleSpecifier": "non-relative", | ||||||
|     "typescript.preferences.importModuleSpecifierEnding": "index", |     "typescript.preferences.importModuleSpecifierEnding": "index", | ||||||
|     "typescript.tsdk": "./web/node_modules/typescript/lib", |     "typescript.tsdk": "./web/node_modules/typescript/lib", | ||||||
|  | |||||||
| @ -17,23 +17,23 @@ diverse, inclusive, and healthy community. | |||||||
| Examples of behavior that contributes to a positive environment for our | Examples of behavior that contributes to a positive environment for our | ||||||
| community include: | community include: | ||||||
|  |  | ||||||
| * Demonstrating empathy and kindness toward other people | -   Demonstrating empathy and kindness toward other people | ||||||
| * Being respectful of differing opinions, viewpoints, and experiences | -   Being respectful of differing opinions, viewpoints, and experiences | ||||||
| * Giving and gracefully accepting constructive feedback | -   Giving and gracefully accepting constructive feedback | ||||||
| * Accepting responsibility and apologizing to those affected by our mistakes, | -   Accepting responsibility and apologizing to those affected by our mistakes, | ||||||
|     and learning from the experience |     and learning from the experience | ||||||
| * Focusing on what is best not just for us as individuals, but for the | -   Focusing on what is best not just for us as individuals, but for the | ||||||
|     overall community |     overall community | ||||||
|  |  | ||||||
| Examples of unacceptable behavior include: | Examples of unacceptable behavior include: | ||||||
|  |  | ||||||
| * The use of sexualized language or imagery, and sexual attention or | -   The use of sexualized language or imagery, and sexual attention or | ||||||
|     advances of any kind |     advances of any kind | ||||||
| * Trolling, insulting or derogatory comments, and personal or political attacks | -   Trolling, insulting or derogatory comments, and personal or political attacks | ||||||
| * Public or private harassment | -   Public or private harassment | ||||||
| * Publishing others' private information, such as a physical or email | -   Publishing others' private information, such as a physical or email | ||||||
|     address, without their explicit permission |     address, without their explicit permission | ||||||
| * Other conduct which could reasonably be considered inappropriate in a | -   Other conduct which could reasonably be considered inappropriate in a | ||||||
|     professional setting |     professional setting | ||||||
|  |  | ||||||
| ## Enforcement Responsibilities | ## Enforcement Responsibilities | ||||||
|  | |||||||
| @ -11,19 +11,22 @@ The following is a set of guidelines for contributing to authentik and its compo | |||||||
| [I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) | [I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) | ||||||
|  |  | ||||||
| [What should I know before I get started?](#what-should-i-know-before-i-get-started) | [What should I know before I get started?](#what-should-i-know-before-i-get-started) | ||||||
|   * [The components](#the-components) |  | ||||||
|   * [authentik's structure](#authentiks-structure) | -   [The components](#the-components) | ||||||
|  | -   [authentik's structure](#authentiks-structure) | ||||||
|  |  | ||||||
| [How Can I Contribute?](#how-can-i-contribute) | [How Can I Contribute?](#how-can-i-contribute) | ||||||
|   * [Reporting Bugs](#reporting-bugs) |  | ||||||
|   * [Suggesting Enhancements](#suggesting-enhancements) | -   [Reporting Bugs](#reporting-bugs) | ||||||
|   * [Your First Code Contribution](#your-first-code-contribution) | -   [Suggesting Enhancements](#suggesting-enhancements) | ||||||
|   * [Pull Requests](#pull-requests) | -   [Your First Code Contribution](#your-first-code-contribution) | ||||||
|  | -   [Pull Requests](#pull-requests) | ||||||
|  |  | ||||||
| [Styleguides](#styleguides) | [Styleguides](#styleguides) | ||||||
|   * [Git Commit Messages](#git-commit-messages) |  | ||||||
|   * [Python Styleguide](#python-styleguide) | -   [Git Commit Messages](#git-commit-messages) | ||||||
|   * [Documentation Styleguide](#documentation-styleguide) | -   [Python Styleguide](#python-styleguide) | ||||||
|  | -   [Documentation Styleguide](#documentation-styleguide) | ||||||
|  |  | ||||||
| ## Code of Conduct | ## Code of Conduct | ||||||
|  |  | ||||||
| @ -39,11 +42,11 @@ Either [create a question on GitHub](https://github.com/goauthentik/authentik/is | |||||||
|  |  | ||||||
| authentik consists of a few larger components: | authentik consists of a few larger components: | ||||||
|  |  | ||||||
| - *authentik* the actual application server, is described below. | -   _authentik_ the actual application server, is described below. | ||||||
| - *outpost-proxy* is a Go application based on a forked version of oauth2_proxy, which does identity-aware reverse proxying. | -   _outpost-proxy_ is a Go application based on a forked version of oauth2_proxy, which does identity-aware reverse proxying. | ||||||
| - *outpost-ldap* is a Go LDAP server that uses the *authentik* application server as its backend | -   _outpost-ldap_ is a Go LDAP server that uses the _authentik_ application server as its backend | ||||||
| - *web* is the web frontend, both for administrating and using authentik. It is written in TypeScript using lit-html and the PatternFly CSS Library. | -   _web_ is the web frontend, both for administrating and using authentik. It is written in TypeScript using lit-html and the PatternFly CSS Library. | ||||||
| - *website* is the Website/documentation, which uses docusaurus. | -   _website_ is the Website/documentation, which uses docusaurus. | ||||||
|  |  | ||||||
| ### authentik's structure | ### authentik's structure | ||||||
|  |  | ||||||
| @ -154,10 +157,10 @@ While the prerequisites above must be satisfied prior to having your pull reques | |||||||
|  |  | ||||||
| ### Git Commit Messages | ### Git Commit Messages | ||||||
|  |  | ||||||
| * Use the format of `<package>: <verb> <description>` | -   Use the format of `<package>: <verb> <description>` | ||||||
|     -   See [here](#authentik-packages) for `package` |     -   See [here](#authentik-packages) for `package` | ||||||
|     -   Example: `providers/saml2: fix parsing of requests` |     -   Example: `providers/saml2: fix parsing of requests` | ||||||
| * Reference issues and pull requests liberally after the first line | -   Reference issues and pull requests liberally after the first line | ||||||
|  |  | ||||||
| ### Python Styleguide | ### Python Styleguide | ||||||
|  |  | ||||||
| @ -165,11 +168,11 @@ All Python code is linted with [black](https://black.readthedocs.io/en/stable/), | |||||||
|  |  | ||||||
| authentik runs on Python 3.9 at the time of writing this. | authentik runs on Python 3.9 at the time of writing this. | ||||||
|  |  | ||||||
| * Use native type-annotations wherever possible. | -   Use native type-annotations wherever possible. | ||||||
| * Add meaningful docstrings when possible. | -   Add meaningful docstrings when possible. | ||||||
| * Ensure any database migrations work properly from the last stable version (this is checked via CI) | -   Ensure any database migrations work properly from the last stable version (this is checked via CI) | ||||||
| * If your code changes central functions, make sure nothing else is broken. | -   If your code changes central functions, make sure nothing else is broken. | ||||||
|  |  | ||||||
| ### Documentation Styleguide | ### Documentation Styleguide | ||||||
|  |  | ||||||
| * Use [MDX](https://mdxjs.com/) whenever appropriate. | -   Use [MDX](https://mdxjs.com/) whenever appropriate. | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ FROM --platform=${BUILDPLATFORM} docker.io/node:18 as website-builder | |||||||
|  |  | ||||||
| COPY ./website /work/website/ | COPY ./website /work/website/ | ||||||
| COPY ./blueprints /work/blueprints/ | COPY ./blueprints /work/blueprints/ | ||||||
|  | COPY ./SECURITY.md /work/ | ||||||
|  |  | ||||||
| ENV NODE_ENV=production | ENV NODE_ENV=production | ||||||
| WORKDIR /work/website | WORKDIR /work/website | ||||||
| @ -62,7 +63,7 @@ COPY --from=poetry-locker /work/requirements-dev.txt / | |||||||
|  |  | ||||||
| RUN apt-get update && \ | RUN apt-get update && \ | ||||||
|     # Required for installing pip packages |     # Required for installing pip packages | ||||||
|     apt-get install -y --no-install-recommends build-essential pkg-config libxmlsec1-dev && \ |     apt-get install -y --no-install-recommends build-essential pkg-config libxmlsec1-dev zlib1g-dev && \ | ||||||
|     # Required for runtime |     # Required for runtime | ||||||
|     apt-get install -y --no-install-recommends libxmlsec1-openssl libmaxminddb0 && \ |     apt-get install -y --no-install-recommends libxmlsec1-openssl libmaxminddb0 && \ | ||||||
|     # Required for bootstrap & healtcheck |     # Required for bootstrap & healtcheck | ||||||
| @ -80,6 +81,7 @@ RUN apt-get update && \ | |||||||
| COPY ./authentik/ /authentik | COPY ./authentik/ /authentik | ||||||
| COPY ./pyproject.toml / | COPY ./pyproject.toml / | ||||||
| COPY ./xml /xml | COPY ./xml /xml | ||||||
|  | COPY ./locale /locale | ||||||
| COPY ./tests /tests | COPY ./tests /tests | ||||||
| COPY ./manage.py / | COPY ./manage.py / | ||||||
| COPY ./blueprints /blueprints | COPY ./blueprints /blueprints | ||||||
|  | |||||||
| @ -25,10 +25,10 @@ For bigger setups, there is a Helm Chart [here](https://github.com/goauthentik/h | |||||||
|  |  | ||||||
| ## Screenshots | ## Screenshots | ||||||
|  |  | ||||||
| Light | Dark | | Light                                                  | Dark                                                  | | ||||||
| --- | --- | | ------------------------------------------------------ | ----------------------------------------------------- | | ||||||
|  |  | |   |   | | ||||||
|  |  | |  |  | | ||||||
|  |  | ||||||
| ## Development | ## Development | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										34
									
								
								SECURITY.md
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								SECURITY.md
									
									
									
									
									
								
							| @ -1,17 +1,43 @@ | |||||||
| # Security Policy | Authentik takes security very seriously. We follow the rules of [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure), and we urge our community to do so as well, instead of reporting vulnerabilities publicly. This allows us to patch the issue quickly, announce it's existence and release the fixed version. | ||||||
|  |  | ||||||
| ## Supported Versions | ## Supported Versions | ||||||
|  |  | ||||||
| (.x being the latest patch release for each version) | (.x being the latest patch release for each version) | ||||||
|  |  | ||||||
| | Version   | Supported          | | | Version   | Supported          | | ||||||
| | ---------- | ------------------ | | | --------- | ------------------ | | ||||||
| | 2022.9.x   | :white_check_mark: | |  | ||||||
| | 2022.10.x | :white_check_mark: | | | 2022.10.x | :white_check_mark: | | ||||||
|  | | 2022.11.x | :white_check_mark: | | ||||||
|  |  | ||||||
| ## Reporting a Vulnerability | ## Reporting a Vulnerability | ||||||
|  |  | ||||||
| To report a vulnerability, send an email to [security@goauthentik.io](mailto:security@goauthentik.io) | To report a vulnerability, send an email to [security@goauthentik.io](mailto:security@goauthentik.io). Be sure to include relevant information like which version you've found the issue in, instructions on how to reproduce the issue, and anything else that might make it easier for us to find the bug. | ||||||
|  |  | ||||||
|  | ## Criticality levels | ||||||
|  |  | ||||||
|  | ### High | ||||||
|  |  | ||||||
|  | -   Authorization bypass | ||||||
|  | -   Circumvention of policies | ||||||
|  |  | ||||||
|  | ### Moderate | ||||||
|  |  | ||||||
|  | -   Denial-of-Service attacks | ||||||
|  |  | ||||||
|  | ### Low | ||||||
|  |  | ||||||
|  | -   Unvalidated redirects | ||||||
|  | -   Issues requiring uncommon setups | ||||||
|  |  | ||||||
|  | ## Disclosure process | ||||||
|  |  | ||||||
|  | 1. Issue is reported via Email as listed above. | ||||||
|  | 2. The authentik Security team will try to reproduce the issue and ask for more information if required. | ||||||
|  | 3. A criticality level is assigned. | ||||||
|  | 4. A fix is created, and if possible tested by the issue reporter. | ||||||
|  | 5. The fix is backported to other supported versions, and if possible a workaround for other versions is created. | ||||||
|  | 6. An announcement is sent out with a fixed release date and criticality level of the issue. The announcement will be sent at least 24 hours before the release of the fix | ||||||
|  | 7. The fixed version is released for the supported versions. | ||||||
|  |  | ||||||
| ## Getting security notifications | ## Getting security notifications | ||||||
|  |  | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| from os import environ | from os import environ | ||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| __version__ = "2022.11.0" | __version__ = "2022.11.2" | ||||||
| ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" | ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -53,6 +53,15 @@ | |||||||
|                     "id": { |                     "id": { | ||||||
|                         "type": "string" |                         "type": "string" | ||||||
|                     }, |                     }, | ||||||
|  |                     "state": { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "enum": [ | ||||||
|  |                             "absent", | ||||||
|  |                             "present", | ||||||
|  |                             "created" | ||||||
|  |                         ], | ||||||
|  |                         "default": "present" | ||||||
|  |                     }, | ||||||
|                     "attrs": { |                     "attrs": { | ||||||
|                         "type": "object", |                         "type": "object", | ||||||
|                         "properties": { |                         "properties": { | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ from django.apps import apps | |||||||
|  |  | ||||||
| from authentik.blueprints.apps import ManagedAppConfig | from authentik.blueprints.apps import ManagedAppConfig | ||||||
| from authentik.blueprints.models import BlueprintInstance | from authentik.blueprints.models import BlueprintInstance | ||||||
| from authentik.lib.config import CONFIG |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def apply_blueprint(*files: str): | def apply_blueprint(*files: str): | ||||||
| @ -46,3 +45,13 @@ def reconcile_app(app_name: str): | |||||||
|         return wrapper |         return wrapper | ||||||
|  |  | ||||||
|     return wrapper_outer |     return wrapper_outer | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def load_yaml_fixture(path: str, **kwargs) -> str: | ||||||
|  |     """Load yaml fixture, optionally formatting it with kwargs""" | ||||||
|  |     with open(Path(__file__).resolve().parent / Path(path), "r", encoding="utf-8") as _fixture: | ||||||
|  |         fixture = _fixture.read() | ||||||
|  |         try: | ||||||
|  |             return fixture % kwargs | ||||||
|  |         except TypeError: | ||||||
|  |             return fixture | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								authentik/blueprints/tests/fixtures/state_absent.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								authentik/blueprints/tests/fixtures/state_absent.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | version: 1 | ||||||
|  | entries: | ||||||
|  | - identifiers: | ||||||
|  |     name: "%(id)s" | ||||||
|  |     slug: "%(id)s" | ||||||
|  |   model: authentik_flows.flow | ||||||
|  |   state: absent | ||||||
							
								
								
									
										10
									
								
								authentik/blueprints/tests/fixtures/state_created.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								authentik/blueprints/tests/fixtures/state_created.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | version: 1 | ||||||
|  | entries: | ||||||
|  | - identifiers: | ||||||
|  |     name: "%(id)s" | ||||||
|  |     slug: "%(id)s" | ||||||
|  |   model: authentik_flows.flow | ||||||
|  |   state: created | ||||||
|  |   attrs: | ||||||
|  |     designation: stage_configuration | ||||||
|  |     title: foo | ||||||
							
								
								
									
										10
									
								
								authentik/blueprints/tests/fixtures/state_present.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								authentik/blueprints/tests/fixtures/state_present.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | version: 1 | ||||||
|  | entries: | ||||||
|  | - identifiers: | ||||||
|  |     name: "%(id)s" | ||||||
|  |     slug: "%(id)s" | ||||||
|  |   model: authentik_flows.flow | ||||||
|  |   state: present | ||||||
|  |   attrs: | ||||||
|  |     designation: stage_configuration | ||||||
|  |     title: foo | ||||||
							
								
								
									
										12
									
								
								authentik/blueprints/tests/fixtures/static_prompt_export.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								authentik/blueprints/tests/fixtures/static_prompt_export.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | version: 1 | ||||||
|  | entries: | ||||||
|  | - identifiers: | ||||||
|  |     pk: cb954fd4-65a5-4ad9-b1ee-180ee9559cf4 | ||||||
|  |   model: authentik_stages_prompt.prompt | ||||||
|  |   attrs: | ||||||
|  |     field_key: username | ||||||
|  |     label: Username | ||||||
|  |     type: username | ||||||
|  |     required: true | ||||||
|  |     placeholder: Username | ||||||
|  |     order: 0 | ||||||
							
								
								
									
										10
									
								
								authentik/blueprints/tests/fixtures/tags.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								authentik/blueprints/tests/fixtures/tags.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | version: 1 | ||||||
|  | context: | ||||||
|  |     foo: bar | ||||||
|  | entries: | ||||||
|  | - attrs: | ||||||
|  |     expression: return True | ||||||
|  |   identifiers: | ||||||
|  |     name: !Format [foo-%s-%s, !Context foo, !Context bar] | ||||||
|  |   id: default-source-enrollment-if-username | ||||||
|  |   model: authentik_policies_expression.expressionpolicy | ||||||
| @ -1,6 +1,7 @@ | |||||||
| """Test blueprints v1""" | """Test blueprints v1""" | ||||||
| from django.test import TransactionTestCase | from django.test import TransactionTestCase | ||||||
|  |  | ||||||
|  | from authentik.blueprints.tests import load_yaml_fixture | ||||||
| from authentik.blueprints.v1.exporter import FlowExporter | from authentik.blueprints.v1.exporter import FlowExporter | ||||||
| from authentik.blueprints.v1.importer import Importer, transaction_rollback | from authentik.blueprints.v1.importer import Importer, transaction_rollback | ||||||
| from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding | from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding | ||||||
| @ -10,32 +11,6 @@ from authentik.policies.models import PolicyBinding | |||||||
| from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage | from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage | ||||||
| from authentik.stages.user_login.models import UserLoginStage | from authentik.stages.user_login.models import UserLoginStage | ||||||
|  |  | ||||||
| STATIC_PROMPT_EXPORT = """version: 1 |  | ||||||
| entries: |  | ||||||
| - identifiers: |  | ||||||
|     pk: cb954fd4-65a5-4ad9-b1ee-180ee9559cf4 |  | ||||||
|   model: authentik_stages_prompt.prompt |  | ||||||
|   attrs: |  | ||||||
|     field_key: username |  | ||||||
|     label: Username |  | ||||||
|     type: username |  | ||||||
|     required: true |  | ||||||
|     placeholder: Username |  | ||||||
|     order: 0 |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| YAML_TAG_TESTS = """version: 1 |  | ||||||
| context: |  | ||||||
|     foo: bar |  | ||||||
| entries: |  | ||||||
| - attrs: |  | ||||||
|     expression: return True |  | ||||||
|   identifiers: |  | ||||||
|     name: !Format [foo-%s-%s, !Context foo, !Context bar] |  | ||||||
|   id: default-source-enrollment-if-username |  | ||||||
|   model: authentik_policies_expression.expressionpolicy |  | ||||||
| """ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestBlueprintsV1(TransactionTestCase): | class TestBlueprintsV1(TransactionTestCase): | ||||||
|     """Test Blueprints""" |     """Test Blueprints""" | ||||||
| @ -85,14 +60,14 @@ class TestBlueprintsV1(TransactionTestCase): | |||||||
|         """Test export and import it twice""" |         """Test export and import it twice""" | ||||||
|         count_initial = Prompt.objects.filter(field_key="username").count() |         count_initial = Prompt.objects.filter(field_key="username").count() | ||||||
|  |  | ||||||
|         importer = Importer(STATIC_PROMPT_EXPORT) |         importer = Importer(load_yaml_fixture("fixtures/static_prompt_export.yaml")) | ||||||
|         self.assertTrue(importer.validate()[0]) |         self.assertTrue(importer.validate()[0]) | ||||||
|         self.assertTrue(importer.apply()) |         self.assertTrue(importer.apply()) | ||||||
|  |  | ||||||
|         count_before = Prompt.objects.filter(field_key="username").count() |         count_before = Prompt.objects.filter(field_key="username").count() | ||||||
|         self.assertEqual(count_initial + 1, count_before) |         self.assertEqual(count_initial + 1, count_before) | ||||||
|  |  | ||||||
|         importer = Importer(STATIC_PROMPT_EXPORT) |         importer = Importer(load_yaml_fixture("fixtures/static_prompt_export.yaml")) | ||||||
|         self.assertTrue(importer.apply()) |         self.assertTrue(importer.apply()) | ||||||
|  |  | ||||||
|         self.assertEqual(Prompt.objects.filter(field_key="username").count(), count_before) |         self.assertEqual(Prompt.objects.filter(field_key="username").count(), count_before) | ||||||
| @ -100,7 +75,7 @@ class TestBlueprintsV1(TransactionTestCase): | |||||||
|     def test_import_yaml_tags(self): |     def test_import_yaml_tags(self): | ||||||
|         """Test some yaml tags""" |         """Test some yaml tags""" | ||||||
|         ExpressionPolicy.objects.filter(name="foo-foo-bar").delete() |         ExpressionPolicy.objects.filter(name="foo-foo-bar").delete() | ||||||
|         importer = Importer(YAML_TAG_TESTS, {"bar": "baz"}) |         importer = Importer(load_yaml_fixture("fixtures/tags.yaml"), {"bar": "baz"}) | ||||||
|         self.assertTrue(importer.validate()[0]) |         self.assertTrue(importer.validate()[0]) | ||||||
|         self.assertTrue(importer.apply()) |         self.assertTrue(importer.apply()) | ||||||
|         self.assertTrue(ExpressionPolicy.objects.filter(name="foo-foo-bar")) |         self.assertTrue(ExpressionPolicy.objects.filter(name="foo-foo-bar")) | ||||||
|  | |||||||
							
								
								
									
										82
									
								
								authentik/blueprints/tests/test_v1_state.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								authentik/blueprints/tests/test_v1_state.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | """Test blueprints v1""" | ||||||
|  | from django.test import TransactionTestCase | ||||||
|  |  | ||||||
|  | from authentik.blueprints.tests import load_yaml_fixture | ||||||
|  | from authentik.blueprints.v1.importer import Importer | ||||||
|  | from authentik.flows.models import Flow | ||||||
|  | from authentik.lib.generators import generate_id | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestBlueprintsV1State(TransactionTestCase): | ||||||
|  |     """Test Blueprints state attribute""" | ||||||
|  |  | ||||||
|  |     def test_state_present(self): | ||||||
|  |         """Test state present""" | ||||||
|  |         flow_slug = generate_id() | ||||||
|  |         import_yaml = load_yaml_fixture("fixtures/state_present.yaml", id=flow_slug) | ||||||
|  |  | ||||||
|  |         importer = Importer(import_yaml) | ||||||
|  |         self.assertTrue(importer.validate()[0]) | ||||||
|  |         self.assertTrue(importer.apply()) | ||||||
|  |         # Ensure object exists | ||||||
|  |         flow: Flow = Flow.objects.filter(slug=flow_slug).first() | ||||||
|  |         self.assertEqual(flow.slug, flow_slug) | ||||||
|  |  | ||||||
|  |         # Update object | ||||||
|  |         flow.title = "bar" | ||||||
|  |         flow.save() | ||||||
|  |  | ||||||
|  |         flow.refresh_from_db() | ||||||
|  |         self.assertEqual(flow.title, "bar") | ||||||
|  |  | ||||||
|  |         # Ensure importer updates it | ||||||
|  |         importer = Importer(import_yaml) | ||||||
|  |         self.assertTrue(importer.validate()[0]) | ||||||
|  |         self.assertTrue(importer.apply()) | ||||||
|  |         flow: Flow = Flow.objects.filter(slug=flow_slug).first() | ||||||
|  |         self.assertEqual(flow.title, "foo") | ||||||
|  |  | ||||||
|  |     def test_state_created(self): | ||||||
|  |         """Test state created""" | ||||||
|  |         flow_slug = generate_id() | ||||||
|  |         import_yaml = load_yaml_fixture("fixtures/state_created.yaml", id=flow_slug) | ||||||
|  |  | ||||||
|  |         importer = Importer(import_yaml) | ||||||
|  |         self.assertTrue(importer.validate()[0]) | ||||||
|  |         self.assertTrue(importer.apply()) | ||||||
|  |         # Ensure object exists | ||||||
|  |         flow: Flow = Flow.objects.filter(slug=flow_slug).first() | ||||||
|  |         self.assertEqual(flow.slug, flow_slug) | ||||||
|  |  | ||||||
|  |         # Update object | ||||||
|  |         flow.title = "bar" | ||||||
|  |         flow.save() | ||||||
|  |  | ||||||
|  |         flow.refresh_from_db() | ||||||
|  |         self.assertEqual(flow.title, "bar") | ||||||
|  |  | ||||||
|  |         # Ensure importer doesn't update it | ||||||
|  |         importer = Importer(import_yaml) | ||||||
|  |         self.assertTrue(importer.validate()[0]) | ||||||
|  |         self.assertTrue(importer.apply()) | ||||||
|  |         flow: Flow = Flow.objects.filter(slug=flow_slug).first() | ||||||
|  |         self.assertEqual(flow.title, "bar") | ||||||
|  |  | ||||||
|  |     def test_state_absent(self): | ||||||
|  |         """Test state absent""" | ||||||
|  |         flow_slug = generate_id() | ||||||
|  |         import_yaml = load_yaml_fixture("fixtures/state_created.yaml", id=flow_slug) | ||||||
|  |  | ||||||
|  |         importer = Importer(import_yaml) | ||||||
|  |         self.assertTrue(importer.validate()[0]) | ||||||
|  |         self.assertTrue(importer.apply()) | ||||||
|  |         # Ensure object exists | ||||||
|  |         flow: Flow = Flow.objects.filter(slug=flow_slug).first() | ||||||
|  |         self.assertEqual(flow.slug, flow_slug) | ||||||
|  |  | ||||||
|  |         import_yaml = load_yaml_fixture("fixtures/state_absent.yaml", id=flow_slug) | ||||||
|  |         importer = Importer(import_yaml) | ||||||
|  |         self.assertTrue(importer.validate()[0]) | ||||||
|  |         self.assertTrue(importer.apply()) | ||||||
|  |         flow: Flow = Flow.objects.filter(slug=flow_slug).first() | ||||||
|  |         self.assertIsNone(flow) | ||||||
| @ -41,11 +41,20 @@ class BlueprintEntryState: | |||||||
|     instance: Optional[Model] = None |     instance: Optional[Model] = None | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BlueprintEntryDesiredState(Enum): | ||||||
|  |     """State an entry should be reconciled to""" | ||||||
|  |  | ||||||
|  |     ABSENT = "absent" | ||||||
|  |     PRESENT = "present" | ||||||
|  |     CREATED = "created" | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass | @dataclass | ||||||
| class BlueprintEntry: | class BlueprintEntry: | ||||||
|     """Single entry of a blueprint""" |     """Single entry of a blueprint""" | ||||||
|  |  | ||||||
|     model: str |     model: str | ||||||
|  |     state: BlueprintEntryDesiredState = field(default=BlueprintEntryDesiredState.PRESENT) | ||||||
|     identifiers: dict[str, Any] = field(default_factory=dict) |     identifiers: dict[str, Any] = field(default_factory=dict) | ||||||
|     attrs: Optional[dict[str, Any]] = field(default_factory=dict) |     attrs: Optional[dict[str, Any]] = field(default_factory=dict) | ||||||
|  |  | ||||||
| @ -227,8 +236,15 @@ class BlueprintDumper(SafeDumper): | |||||||
|         self.add_representer(UUID, lambda self, data: self.represent_str(str(data))) |         self.add_representer(UUID, lambda self, data: self.represent_str(str(data))) | ||||||
|         self.add_representer(OrderedDict, lambda self, data: self.represent_dict(dict(data))) |         self.add_representer(OrderedDict, lambda self, data: self.represent_dict(dict(data))) | ||||||
|         self.add_representer(Enum, lambda self, data: self.represent_str(data.value)) |         self.add_representer(Enum, lambda self, data: self.represent_str(data.value)) | ||||||
|  |         self.add_representer( | ||||||
|  |             BlueprintEntryDesiredState, lambda self, data: self.represent_str(data.value) | ||||||
|  |         ) | ||||||
|         self.add_representer(None, lambda self, data: self.represent_str(str(data))) |         self.add_representer(None, lambda self, data: self.represent_str(str(data))) | ||||||
|  |  | ||||||
|  |     def ignore_aliases(self, data): | ||||||
|  |         """Don't use any YAML anchors""" | ||||||
|  |         return True | ||||||
|  |  | ||||||
|     def represent(self, data) -> None: |     def represent(self, data) -> None: | ||||||
|         if is_dataclass(data): |         if is_dataclass(data): | ||||||
|  |  | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ from contextlib import contextmanager | |||||||
| from copy import deepcopy | from copy import deepcopy | ||||||
| from typing import Any, Optional | from typing import Any, Optional | ||||||
|  |  | ||||||
|  | from dacite.config import Config | ||||||
| from dacite.core import from_dict | from dacite.core import from_dict | ||||||
| from dacite.exceptions import DaciteError | from dacite.exceptions import DaciteError | ||||||
| from deepmerge import always_merger | from deepmerge import always_merger | ||||||
| @ -20,6 +21,7 @@ from yaml import load | |||||||
| from authentik.blueprints.v1.common import ( | from authentik.blueprints.v1.common import ( | ||||||
|     Blueprint, |     Blueprint, | ||||||
|     BlueprintEntry, |     BlueprintEntry, | ||||||
|  |     BlueprintEntryDesiredState, | ||||||
|     BlueprintEntryState, |     BlueprintEntryState, | ||||||
|     BlueprintLoader, |     BlueprintLoader, | ||||||
|     EntryInvalidError, |     EntryInvalidError, | ||||||
| @ -82,7 +84,9 @@ class Importer: | |||||||
|         self.logger = get_logger() |         self.logger = get_logger() | ||||||
|         import_dict = load(yaml_input, BlueprintLoader) |         import_dict = load(yaml_input, BlueprintLoader) | ||||||
|         try: |         try: | ||||||
|             self.__import = from_dict(Blueprint, import_dict) |             self.__import = from_dict( | ||||||
|  |                 Blueprint, import_dict, config=Config(cast=[BlueprintEntryDesiredState]) | ||||||
|  |             ) | ||||||
|         except DaciteError as exc: |         except DaciteError as exc: | ||||||
|             raise EntryInvalidError from exc |             raise EntryInvalidError from exc | ||||||
|         ctx = {} |         ctx = {} | ||||||
| @ -135,7 +139,7 @@ class Importer: | |||||||
|             sub_query &= Q(**{identifier: value}) |             sub_query &= Q(**{identifier: value}) | ||||||
|         return main_query | sub_query |         return main_query | sub_query | ||||||
|  |  | ||||||
|     def _validate_single(self, entry: BlueprintEntry) -> BaseSerializer: |     def _validate_single(self, entry: BlueprintEntry) -> Optional[BaseSerializer]: | ||||||
|         """Validate a single entry""" |         """Validate a single entry""" | ||||||
|         model_app_label, model_name = entry.model.split(".") |         model_app_label, model_name = entry.model.split(".") | ||||||
|         model: type[SerializerModel] = registry.get_model(model_app_label, model_name) |         model: type[SerializerModel] = registry.get_model(model_app_label, model_name) | ||||||
| @ -168,8 +172,11 @@ class Importer: | |||||||
|         existing_models = model.objects.filter(self.__query_from_identifier(updated_identifiers)) |         existing_models = model.objects.filter(self.__query_from_identifier(updated_identifiers)) | ||||||
|  |  | ||||||
|         serializer_kwargs = {} |         serializer_kwargs = {} | ||||||
|         if not isinstance(model(), BaseMetaModel) and existing_models.exists(): |  | ||||||
|         model_instance = existing_models.first() |         model_instance = existing_models.first() | ||||||
|  |         if not isinstance(model(), BaseMetaModel) and model_instance: | ||||||
|  |             if entry.state == BlueprintEntryDesiredState.CREATED: | ||||||
|  |                 self.logger.debug("instance exists, skipping") | ||||||
|  |                 return None | ||||||
|             self.logger.debug( |             self.logger.debug( | ||||||
|                 "initialise serializer with instance", |                 "initialise serializer with instance", | ||||||
|                 model=model, |                 model=model, | ||||||
| @ -234,12 +241,25 @@ class Importer: | |||||||
|             except EntryInvalidError as exc: |             except EntryInvalidError as exc: | ||||||
|                 self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc) |                 self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc) | ||||||
|                 return False |                 return False | ||||||
|  |             if not serializer: | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             if entry.state in [ | ||||||
|  |                 BlueprintEntryDesiredState.PRESENT, | ||||||
|  |                 BlueprintEntryDesiredState.CREATED, | ||||||
|  |             ]: | ||||||
|                 model = serializer.save() |                 model = serializer.save() | ||||||
|                 if "pk" in entry.identifiers: |                 if "pk" in entry.identifiers: | ||||||
|                     self.__pk_map[entry.identifiers["pk"]] = model.pk |                     self.__pk_map[entry.identifiers["pk"]] = model.pk | ||||||
|                 entry._state = BlueprintEntryState(model) |                 entry._state = BlueprintEntryState(model) | ||||||
|                 self.logger.debug("updated model", model=model) |                 self.logger.debug("updated model", model=model) | ||||||
|  |             elif entry.state == BlueprintEntryDesiredState.ABSENT: | ||||||
|  |                 instance: Optional[Model] = serializer.instance | ||||||
|  |                 if instance: | ||||||
|  |                     instance.delete() | ||||||
|  |                     self.logger.debug("deleted model", mode=instance) | ||||||
|  |                     continue | ||||||
|  |                 self.logger.debug("entry to delete with no instance, skipping") | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
|     def validate(self) -> tuple[bool, list[EventDict]]: |     def validate(self) -> tuple[bool, list[EventDict]]: | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| """authentik shell command""" | """authentik shell command""" | ||||||
| import code | import code | ||||||
| import platform | import platform | ||||||
|  | import sys | ||||||
|  | import traceback | ||||||
|  |  | ||||||
| from django.apps import apps | from django.apps import apps | ||||||
| from django.core.management.base import BaseCommand | from django.core.management.base import BaseCommand | ||||||
| @ -89,6 +91,21 @@ class Command(BaseCommand): | |||||||
|             exec(options["command"], namespace)  # nosec # noqa |             exec(options["command"], namespace)  # nosec # noqa | ||||||
|             return |             return | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             hook = sys.__interactivehook__ | ||||||
|  |         except AttributeError: | ||||||
|  |             # Match the behavior of the cpython shell where a missing | ||||||
|  |             # sys.__interactivehook__ is ignored. | ||||||
|  |             pass | ||||||
|  |         else: | ||||||
|  |             try: | ||||||
|  |                 hook() | ||||||
|  |             except Exception:  # pylint: disable=broad-except | ||||||
|  |                 # Match the behavior of the cpython shell where an error in | ||||||
|  |                 # sys.__interactivehook__ prints a warning and the exception | ||||||
|  |                 # and continues. | ||||||
|  |                 print("Failed calling sys.__interactivehook__") | ||||||
|  |                 traceback.print_exc() | ||||||
|         # Try to enable tab-complete |         # Try to enable tab-complete | ||||||
|         try: |         try: | ||||||
|             import readline |             import readline | ||||||
|  | |||||||
| @ -71,6 +71,7 @@ class FlowSerializer(ModelSerializer): | |||||||
|             "export_url", |             "export_url", | ||||||
|             "layout", |             "layout", | ||||||
|             "denied_action", |             "denied_action", | ||||||
|  |             "authentication", | ||||||
|         ] |         ] | ||||||
|         extra_kwargs = { |         extra_kwargs = { | ||||||
|             "background": {"read_only": True}, |             "background": {"read_only": True}, | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| """flow exceptions""" | """flow exceptions""" | ||||||
|  | from typing import Optional | ||||||
|  |  | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import gettext_lazy as _ | ||||||
|  |  | ||||||
| from authentik.lib.sentry import SentryIgnoredException | from authentik.lib.sentry import SentryIgnoredException | ||||||
| @ -6,15 +8,15 @@ from authentik.policies.types import PolicyResult | |||||||
|  |  | ||||||
|  |  | ||||||
| class FlowNonApplicableException(SentryIgnoredException): | class FlowNonApplicableException(SentryIgnoredException): | ||||||
|     """Flow does not apply to current user (denied by policy).""" |     """Flow does not apply to current user (denied by policy, or otherwise).""" | ||||||
|  |  | ||||||
|     policy_result: PolicyResult |     policy_result: Optional[PolicyResult] = None | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def messages(self) -> str: |     def messages(self) -> str: | ||||||
|         """Get messages from policy result, fallback to generic reason""" |         """Get messages from policy result, fallback to generic reason""" | ||||||
|         if len(self.policy_result.messages) < 1: |         if not self.policy_result or len(self.policy_result.messages) < 1: | ||||||
|             return _("Flow does not apply to current user (denied by policy).") |             return _("Flow does not apply to current user.") | ||||||
|         return "\n".join(self.policy_result.messages) |         return "\n".join(self.policy_result.messages) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								authentik/flows/migrations/0024_flow_authentication.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								authentik/flows/migrations/0024_flow_authentication.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | # Generated by Django 4.1.3 on 2022-11-30 09:04 | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("authentik_flows", "0023_flow_denied_action"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name="flow", | ||||||
|  |             name="authentication", | ||||||
|  |             field=models.TextField( | ||||||
|  |                 choices=[ | ||||||
|  |                     ("none", "None"), | ||||||
|  |                     ("require_authenticated", "Require Authenticated"), | ||||||
|  |                     ("require_unauthenticated", "Require Unauthenticated"), | ||||||
|  |                     ("require_superuser", "Require Superuser"), | ||||||
|  |                 ], | ||||||
|  |                 default="none", | ||||||
|  |                 help_text="Required level of authentication and authorization to access a flow.", | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -23,6 +23,15 @@ if TYPE_CHECKING: | |||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class FlowAuthenticationRequirement(models.TextChoices): | ||||||
|  |     """Required level of authentication and authorization to access a flow""" | ||||||
|  |  | ||||||
|  |     NONE = "none" | ||||||
|  |     REQUIRE_AUTHENTICATED = "require_authenticated" | ||||||
|  |     REQUIRE_UNAUTHENTICATED = "require_unauthenticated" | ||||||
|  |     REQUIRE_SUPERUSER = "require_superuser" | ||||||
|  |  | ||||||
|  |  | ||||||
| class NotConfiguredAction(models.TextChoices): | class NotConfiguredAction(models.TextChoices): | ||||||
|     """Decides how the FlowExecutor should proceed when a stage isn't configured""" |     """Decides how the FlowExecutor should proceed when a stage isn't configured""" | ||||||
|  |  | ||||||
| @ -152,6 +161,12 @@ class Flow(SerializerModel, PolicyBindingModel): | |||||||
|         help_text=_("Configure what should happen when a flow denies access to a user."), |         help_text=_("Configure what should happen when a flow denies access to a user."), | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |     authentication = models.TextField( | ||||||
|  |         choices=FlowAuthenticationRequirement.choices, | ||||||
|  |         default=FlowAuthenticationRequirement.NONE, | ||||||
|  |         help_text=_("Required level of authentication and authorization to access a flow."), | ||||||
|  |     ) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def background_url(self) -> str: |     def background_url(self) -> str: | ||||||
|         """Get the URL to the background image. If the name is /static or starts with http |         """Get the URL to the background image. If the name is /static or starts with http | ||||||
|  | |||||||
| @ -13,7 +13,14 @@ from authentik.events.models import cleanse_dict | |||||||
| from authentik.flows.apps import HIST_FLOWS_PLAN_TIME | from authentik.flows.apps import HIST_FLOWS_PLAN_TIME | ||||||
| from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException | from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException | ||||||
| from authentik.flows.markers import ReevaluateMarker, StageMarker | from authentik.flows.markers import ReevaluateMarker, StageMarker | ||||||
| from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage, in_memory_stage | from authentik.flows.models import ( | ||||||
|  |     Flow, | ||||||
|  |     FlowAuthenticationRequirement, | ||||||
|  |     FlowDesignation, | ||||||
|  |     FlowStageBinding, | ||||||
|  |     Stage, | ||||||
|  |     in_memory_stage, | ||||||
|  | ) | ||||||
| from authentik.lib.config import CONFIG | from authentik.lib.config import CONFIG | ||||||
| from authentik.policies.engine import PolicyEngine | from authentik.policies.engine import PolicyEngine | ||||||
|  |  | ||||||
| @ -117,11 +124,30 @@ class FlowPlanner: | |||||||
|         self.flow = flow |         self.flow = flow | ||||||
|         self._logger = get_logger().bind(flow_slug=flow.slug) |         self._logger = get_logger().bind(flow_slug=flow.slug) | ||||||
|  |  | ||||||
|  |     def _check_authentication(self, request: HttpRequest): | ||||||
|  |         """Check the flow's authentication level is matched by `request`""" | ||||||
|  |         if ( | ||||||
|  |             self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_AUTHENTICATED | ||||||
|  |             and not request.user.is_authenticated | ||||||
|  |         ): | ||||||
|  |             raise FlowNonApplicableException() | ||||||
|  |         if ( | ||||||
|  |             self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_UNAUTHENTICATED | ||||||
|  |             and request.user.is_authenticated | ||||||
|  |         ): | ||||||
|  |             raise FlowNonApplicableException() | ||||||
|  |         if ( | ||||||
|  |             self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_SUPERUSER | ||||||
|  |             and not request.user.is_superuser | ||||||
|  |         ): | ||||||
|  |             raise FlowNonApplicableException() | ||||||
|  |  | ||||||
|     def plan( |     def plan( | ||||||
|         self, request: HttpRequest, default_context: Optional[dict[str, Any]] = None |         self, request: HttpRequest, default_context: Optional[dict[str, Any]] = None | ||||||
|     ) -> FlowPlan: |     ) -> FlowPlan: | ||||||
|         """Check each of the flows' policies, check policies for each stage with PolicyBinding |         """Check each of the flows' policies, check policies for each stage with PolicyBinding | ||||||
|         and return ordered list""" |         and return ordered list""" | ||||||
|  |         self._check_authentication(request) | ||||||
|         with Hub.current.start_span( |         with Hub.current.start_span( | ||||||
|             op="authentik.flow.planner.plan", description=self.flow.slug |             op="authentik.flow.planner.plan", description=self.flow.slug | ||||||
|         ) as span: |         ) as span: | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| """flow planner tests""" | """flow planner tests""" | ||||||
| from unittest.mock import MagicMock, Mock, PropertyMock, patch | from unittest.mock import MagicMock, Mock, PropertyMock, patch | ||||||
|  |  | ||||||
|  | from django.contrib.auth.models import AnonymousUser | ||||||
| from django.contrib.sessions.middleware import SessionMiddleware | from django.contrib.sessions.middleware import SessionMiddleware | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.test import RequestFactory, TestCase | from django.test import RequestFactory, TestCase | ||||||
| @ -8,10 +9,10 @@ from django.urls import reverse | |||||||
| from guardian.shortcuts import get_anonymous_user | from guardian.shortcuts import get_anonymous_user | ||||||
|  |  | ||||||
| from authentik.core.models import User | from authentik.core.models import User | ||||||
| from authentik.core.tests.utils import create_test_flow | from authentik.core.tests.utils import create_test_admin_user, create_test_flow | ||||||
| from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException | from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException | ||||||
| from authentik.flows.markers import ReevaluateMarker, StageMarker | from authentik.flows.markers import ReevaluateMarker, StageMarker | ||||||
| from authentik.flows.models import FlowDesignation, FlowStageBinding | from authentik.flows.models import FlowAuthenticationRequirement, FlowDesignation, FlowStageBinding | ||||||
| from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key | from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key | ||||||
| from authentik.lib.tests.utils import dummy_get_response | from authentik.lib.tests.utils import dummy_get_response | ||||||
| from authentik.policies.dummy.models import DummyPolicy | from authentik.policies.dummy.models import DummyPolicy | ||||||
| @ -43,6 +44,30 @@ class TestFlowPlanner(TestCase): | |||||||
|             planner = FlowPlanner(flow) |             planner = FlowPlanner(flow) | ||||||
|             planner.plan(request) |             planner.plan(request) | ||||||
|  |  | ||||||
|  |     def test_authentication(self): | ||||||
|  |         """Test flow authentication""" | ||||||
|  |         flow = create_test_flow() | ||||||
|  |         flow.authentication = FlowAuthenticationRequirement.NONE | ||||||
|  |         request = self.request_factory.get( | ||||||
|  |             reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), | ||||||
|  |         ) | ||||||
|  |         request.user = AnonymousUser() | ||||||
|  |         planner = FlowPlanner(flow) | ||||||
|  |         planner.allow_empty_flows = True | ||||||
|  |         planner.plan(request) | ||||||
|  |  | ||||||
|  |         with self.assertRaises(FlowNonApplicableException): | ||||||
|  |             flow.authentication = FlowAuthenticationRequirement.REQUIRE_AUTHENTICATED | ||||||
|  |             FlowPlanner(flow).plan(request) | ||||||
|  |         with self.assertRaises(FlowNonApplicableException): | ||||||
|  |             flow.authentication = FlowAuthenticationRequirement.REQUIRE_SUPERUSER | ||||||
|  |             FlowPlanner(flow).plan(request) | ||||||
|  |  | ||||||
|  |         request.user = create_test_admin_user() | ||||||
|  |         planner = FlowPlanner(flow) | ||||||
|  |         planner.allow_empty_flows = True | ||||||
|  |         planner.plan(request) | ||||||
|  |  | ||||||
|     @patch( |     @patch( | ||||||
|         "authentik.policies.engine.PolicyEngine.result", |         "authentik.policies.engine.PolicyEngine.result", | ||||||
|         POLICY_RETURN_FALSE, |         POLICY_RETURN_FALSE, | ||||||
|  | |||||||
| @ -202,10 +202,10 @@ class ResponseProcessor: | |||||||
|         """Get all attributes sent""" |         """Get all attributes sent""" | ||||||
|         attributes = {} |         attributes = {} | ||||||
|         assertion = self._root.find(f"{{{NS_SAML_ASSERTION}}}Assertion") |         assertion = self._root.find(f"{{{NS_SAML_ASSERTION}}}Assertion") | ||||||
|         if not assertion: |         if assertion is None: | ||||||
|             raise ValueError("Assertion element not found") |             raise ValueError("Assertion element not found") | ||||||
|         attribute_statement = assertion.find(f"{{{NS_SAML_ASSERTION}}}AttributeStatement") |         attribute_statement = assertion.find(f"{{{NS_SAML_ASSERTION}}}AttributeStatement") | ||||||
|         if not attribute_statement: |         if attribute_statement is None: | ||||||
|             raise ValueError("Attribute statement element not found") |             raise ValueError("Attribute statement element not found") | ||||||
|         # Get all attributes and their values into a dict |         # Get all attributes and their values into a dict | ||||||
|         for attribute in attribute_statement.iterchildren(): |         for attribute in attribute_statement.iterchildren(): | ||||||
|  | |||||||
| @ -118,12 +118,12 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet): | |||||||
|             .first() |             .first() | ||||||
|         ) |         ) | ||||||
|         if not user: |         if not user: | ||||||
|             return Response(data={"non_field_errors": ["user does not exist"]}, status=400) |             return Response(data={"non_field_errors": ["User does not exist."]}, status=400) | ||||||
|         device = DuoDevice.objects.filter( |         device = DuoDevice.objects.filter( | ||||||
|             duo_user_id=request.data.get("duo_user_id"), user=user, stage=stage |             duo_user_id=request.data.get("duo_user_id"), user=user, stage=stage | ||||||
|         ).first() |         ).first() | ||||||
|         if device: |         if device: | ||||||
|             return Response(data={"non_field_errors": ["device exists already"]}, status=400) |             return Response(data={"non_field_errors": ["Device exists already."]}, status=400) | ||||||
|         DuoDevice.objects.create( |         DuoDevice.objects.create( | ||||||
|             duo_user_id=request.data.get("duo_user_id"), |             duo_user_id=request.data.get("duo_user_id"), | ||||||
|             user=user, |             user=user, | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| """Test validator stage""" | """Test validator stage""" | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
| from hashlib import sha256 | from hashlib import sha256 | ||||||
| from http.cookies import SimpleCookie |  | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| @ -76,7 +75,7 @@ class AuthenticatorValidateStageTOTPTests(FlowTestCase): | |||||||
|             component="ak-stage-authenticator-validate", |             component="ak-stage-authenticator-validate", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def test_last_auth_threshold_valid(self) -> SimpleCookie: |     def test_last_auth_threshold_valid(self): | ||||||
|         """Test last_auth_threshold""" |         """Test last_auth_threshold""" | ||||||
|         ident_stage = IdentificationStage.objects.create( |         ident_stage = IdentificationStage.objects.create( | ||||||
|             name=generate_id(), |             name=generate_id(), | ||||||
| @ -115,12 +114,47 @@ class AuthenticatorValidateStageTOTPTests(FlowTestCase): | |||||||
|         ) |         ) | ||||||
|         self.assertIn(COOKIE_NAME_MFA, response.cookies) |         self.assertIn(COOKIE_NAME_MFA, response.cookies) | ||||||
|         self.assertStageResponse(response, component="xak-flow-redirect", to="/") |         self.assertStageResponse(response, component="xak-flow-redirect", to="/") | ||||||
|         return response.cookies |  | ||||||
|  |  | ||||||
|     def test_last_auth_skip(self): |     def test_last_auth_skip(self): | ||||||
|         """Test valid cookie""" |         """Test valid cookie""" | ||||||
|         cookies = self.test_last_auth_threshold_valid() |         ident_stage = IdentificationStage.objects.create( | ||||||
|         mfa_cookie = cookies[COOKIE_NAME_MFA] |             name=generate_id(), | ||||||
|  |             user_fields=[ | ||||||
|  |                 UserFields.USERNAME, | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  |         device: TOTPDevice = TOTPDevice.objects.create( | ||||||
|  |             user=self.user, | ||||||
|  |             confirmed=True, | ||||||
|  |         ) | ||||||
|  |         stage = AuthenticatorValidateStage.objects.create( | ||||||
|  |             name=generate_id(), | ||||||
|  |             last_auth_threshold="hours=1", | ||||||
|  |             not_configured_action=NotConfiguredAction.CONFIGURE, | ||||||
|  |             device_classes=[DeviceClasses.TOTP], | ||||||
|  |         ) | ||||||
|  |         stage.configuration_stages.set([ident_stage]) | ||||||
|  |         FlowStageBinding.objects.create(target=self.flow, stage=ident_stage, order=0) | ||||||
|  |         FlowStageBinding.objects.create(target=self.flow, stage=stage, order=1) | ||||||
|  |  | ||||||
|  |         response = self.client.post( | ||||||
|  |             reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), | ||||||
|  |             {"uid_field": self.user.username}, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(response.status_code, 302) | ||||||
|  |         response = self.client.get( | ||||||
|  |             reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), | ||||||
|  |         ) | ||||||
|  |         # Verify token once here to set last_t etc | ||||||
|  |         totp = TOTP(device.bin_key) | ||||||
|  |         sleep(1) | ||||||
|  |         response = self.client.post( | ||||||
|  |             reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), | ||||||
|  |             {"code": str(totp.token())}, | ||||||
|  |         ) | ||||||
|  |         self.assertIn(COOKIE_NAME_MFA, response.cookies) | ||||||
|  |         self.assertStageResponse(response, component="xak-flow-redirect", to="/") | ||||||
|  |         mfa_cookie = response.cookies[COOKIE_NAME_MFA] | ||||||
|         self.client.logout() |         self.client.logout() | ||||||
|         self.client.cookies[COOKIE_NAME_MFA] = mfa_cookie |         self.client.cookies[COOKIE_NAME_MFA] = mfa_cookie | ||||||
|         response = self.client.post( |         response = self.client.post( | ||||||
|  | |||||||
| @ -44,6 +44,28 @@ class TestEmailStage(FlowTestCase): | |||||||
|         response = self.client.get(url) |         response = self.client.get(url) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|  |     @patch( | ||||||
|  |         "authentik.stages.email.models.EmailStage.backend_class", | ||||||
|  |         PropertyMock(return_value=EmailBackend), | ||||||
|  |     ) | ||||||
|  |     def test_rendering_locale(self): | ||||||
|  |         """Test with pending user""" | ||||||
|  |         self.user.attributes = {"settings": {"locale": "de"}} | ||||||
|  |         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) | ||||||
|  |         plan.context[PLAN_CONTEXT_PENDING_USER] = self.user | ||||||
|  |         session = self.client.session | ||||||
|  |         session[SESSION_KEY_PLAN] = plan | ||||||
|  |         session.save() | ||||||
|  |  | ||||||
|  |         url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) | ||||||
|  |         response = self.client.get(url) | ||||||
|  |         self.assertEqual(response.status_code, 200) | ||||||
|  |         self.assertEqual(len(mail.outbox), 1) | ||||||
|  |         self.assertEqual(mail.outbox[0].subject, "authentik") | ||||||
|  |         self.assertNotIn( | ||||||
|  |             "You recently requested to change your password", mail.outbox[0].alternatives[0][0] | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     def test_without_user(self): |     def test_without_user(self): | ||||||
|         """Test without pending user""" |         """Test without pending user""" | ||||||
|         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) |         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) | ||||||
| @ -55,6 +77,10 @@ class TestEmailStage(FlowTestCase): | |||||||
|         response = self.client.get(url) |         response = self.client.get(url) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|  |  | ||||||
|  |     @patch( | ||||||
|  |         "authentik.stages.email.models.EmailStage.backend_class", | ||||||
|  |         PropertyMock(return_value=EmailBackend), | ||||||
|  |     ) | ||||||
|     def test_pending_user(self): |     def test_pending_user(self): | ||||||
|         """Test with pending user""" |         """Test with pending user""" | ||||||
|         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) |         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) | ||||||
| @ -64,16 +90,16 @@ class TestEmailStage(FlowTestCase): | |||||||
|         session.save() |         session.save() | ||||||
|  |  | ||||||
|         url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) |         url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) | ||||||
|         with patch( |  | ||||||
|             "authentik.stages.email.models.EmailStage.backend_class", |  | ||||||
|             PropertyMock(return_value=EmailBackend), |  | ||||||
|         ): |  | ||||||
|         response = self.client.post(url) |         response = self.client.post(url) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertEqual(len(mail.outbox), 1) |         self.assertEqual(len(mail.outbox), 1) | ||||||
|         self.assertEqual(mail.outbox[0].subject, "authentik") |         self.assertEqual(mail.outbox[0].subject, "authentik") | ||||||
|         self.assertEqual(mail.outbox[0].to, [self.user.email]) |         self.assertEqual(mail.outbox[0].to, [self.user.email]) | ||||||
|  |  | ||||||
|  |     @patch( | ||||||
|  |         "authentik.stages.email.models.EmailStage.backend_class", | ||||||
|  |         PropertyMock(return_value=EmailBackend), | ||||||
|  |     ) | ||||||
|     def test_pending_user_override(self): |     def test_pending_user_override(self): | ||||||
|         """Test with pending user (override to)""" |         """Test with pending user (override to)""" | ||||||
|         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) |         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) | ||||||
| @ -84,23 +110,19 @@ class TestEmailStage(FlowTestCase): | |||||||
|         session.save() |         session.save() | ||||||
|  |  | ||||||
|         url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) |         url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) | ||||||
|         with patch( |  | ||||||
|             "authentik.stages.email.models.EmailStage.backend_class", |  | ||||||
|             PropertyMock(return_value=EmailBackend), |  | ||||||
|         ): |  | ||||||
|         response = self.client.post(url) |         response = self.client.post(url) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertEqual(len(mail.outbox), 1) |         self.assertEqual(len(mail.outbox), 1) | ||||||
|         self.assertEqual(mail.outbox[0].subject, "authentik") |         self.assertEqual(mail.outbox[0].subject, "authentik") | ||||||
|         self.assertEqual(mail.outbox[0].to, ["foo@bar.baz"]) |         self.assertEqual(mail.outbox[0].to, ["foo@bar.baz"]) | ||||||
|  |  | ||||||
|  |     @patch( | ||||||
|  |         "authentik.stages.email.models.EmailStage.backend_class", | ||||||
|  |         PropertyMock(return_value=SMTPEmailBackend), | ||||||
|  |     ) | ||||||
|     def test_use_global_settings(self): |     def test_use_global_settings(self): | ||||||
|         """Test use_global_settings""" |         """Test use_global_settings""" | ||||||
|         host = "some-unique-string" |         host = "some-unique-string" | ||||||
|         with patch( |  | ||||||
|             "authentik.stages.email.models.EmailStage.backend_class", |  | ||||||
|             PropertyMock(return_value=SMTPEmailBackend), |  | ||||||
|         ): |  | ||||||
|         with self.settings(EMAIL_HOST=host): |         with self.settings(EMAIL_HOST=host): | ||||||
|             self.assertEqual(EmailStage(use_global_settings=True).backend.host, host) |             self.assertEqual(EmailStage(use_global_settings=True).backend.host, host) | ||||||
|  |  | ||||||
|  | |||||||
| @ -137,7 +137,7 @@ class TestPromptStage(FlowTestCase): | |||||||
|             self.assertIn(prompt.label, response.content.decode()) |             self.assertIn(prompt.label, response.content.decode()) | ||||||
|             self.assertIn(prompt.placeholder, response.content.decode()) |             self.assertIn(prompt.placeholder, response.content.decode()) | ||||||
|  |  | ||||||
|     def test_valid_challenge_with_policy(self) -> PromptChallengeResponse: |     def test_valid_challenge_with_policy(self): | ||||||
|         """Test challenge_response validation""" |         """Test challenge_response validation""" | ||||||
|         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) |         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) | ||||||
|         expr = ( |         expr = ( | ||||||
| @ -151,9 +151,8 @@ class TestPromptStage(FlowTestCase): | |||||||
|             None, stage=self.stage, plan=plan, data=self.prompt_data |             None, stage=self.stage, plan=plan, data=self.prompt_data | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(challenge_response.is_valid(), True) |         self.assertEqual(challenge_response.is_valid(), True) | ||||||
|         return challenge_response |  | ||||||
|  |  | ||||||
|     def test_invalid_challenge(self) -> PromptChallengeResponse: |     def test_invalid_challenge(self): | ||||||
|         """Test challenge_response validation""" |         """Test challenge_response validation""" | ||||||
|         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) |         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) | ||||||
|         expr = "False" |         expr = "False" | ||||||
| @ -164,7 +163,6 @@ class TestPromptStage(FlowTestCase): | |||||||
|             None, stage=self.stage, plan=plan, data=self.prompt_data |             None, stage=self.stage, plan=plan, data=self.prompt_data | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(challenge_response.is_valid(), False) |         self.assertEqual(challenge_response.is_valid(), False) | ||||||
|         return challenge_response |  | ||||||
|  |  | ||||||
|     def test_valid_challenge_request(self): |     def test_valid_challenge_request(self): | ||||||
|         """Test a request with valid challenge_response data""" |         """Test a request with valid challenge_response data""" | ||||||
| @ -173,7 +171,18 @@ class TestPromptStage(FlowTestCase): | |||||||
|         session[SESSION_KEY_PLAN] = plan |         session[SESSION_KEY_PLAN] = plan | ||||||
|         session.save() |         session.save() | ||||||
|  |  | ||||||
|         challenge_response = self.test_valid_challenge_with_policy() |         plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) | ||||||
|  |         expr = ( | ||||||
|  |             "return request.context['prompt_data']['password_prompt'] " | ||||||
|  |             "== request.context['prompt_data']['password2_prompt']" | ||||||
|  |         ) | ||||||
|  |         expr_policy = ExpressionPolicy.objects.create(name="validate-form", expression=expr) | ||||||
|  |         self.stage.validation_policies.set([expr_policy]) | ||||||
|  |         self.stage.save() | ||||||
|  |         challenge_response = PromptChallengeResponse( | ||||||
|  |             None, stage=self.stage, plan=plan, data=self.prompt_data | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(challenge_response.is_valid(), True) | ||||||
|  |  | ||||||
|         with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()): |         with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()): | ||||||
|             response = self.client.post( |             response = self.client.post( | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: stage_configuration |     designation: stage_configuration | ||||||
|     name: Change Password |     name: Change Password | ||||||
|     title: Change password |     title: Change password | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-password-change |     slug: default-password-change | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ entries: | |||||||
|     designation: authentication |     designation: authentication | ||||||
|     name: Welcome to authentik! |     name: Welcome to authentik! | ||||||
|     title: Welcome to authentik! |     title: Welcome to authentik! | ||||||
|  |     authentication: require_unauthenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-authentication-flow |     slug: default-authentication-flow | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: invalidation |     designation: invalidation | ||||||
|     name: Logout |     name: Logout | ||||||
|     title: Default Invalidation Flow |     title: Default Invalidation Flow | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-invalidation-flow |     slug: default-invalidation-flow | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: stage_configuration |     designation: stage_configuration | ||||||
|     name: default-authenticator-static-setup |     name: default-authenticator-static-setup | ||||||
|     title: Setup Static OTP Tokens |     title: Setup Static OTP Tokens | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-authenticator-static-setup |     slug: default-authenticator-static-setup | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: stage_configuration |     designation: stage_configuration | ||||||
|     name: default-authenticator-totp-setup |     name: default-authenticator-totp-setup | ||||||
|     title: Setup Two-Factor authentication |     title: Setup Two-Factor authentication | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-authenticator-totp-setup |     slug: default-authenticator-totp-setup | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: stage_configuration |     designation: stage_configuration | ||||||
|     name: default-authenticator-webauthn-setup |     name: default-authenticator-webauthn-setup | ||||||
|     title: Setup WebAuthn |     title: Setup WebAuthn | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-authenticator-webauthn-setup |     slug: default-authenticator-webauthn-setup | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: authorization |     designation: authorization | ||||||
|     name: Authorize Application |     name: Authorize Application | ||||||
|     title: Redirecting to %(app)s |     title: Redirecting to %(app)s | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-provider-authorization-explicit-consent |     slug: default-provider-authorization-explicit-consent | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: authorization |     designation: authorization | ||||||
|     name: Authorize Application |     name: Authorize Application | ||||||
|     title: Redirecting to %(app)s |     title: Redirecting to %(app)s | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-provider-authorization-implicit-consent |     slug: default-provider-authorization-implicit-consent | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: authentication |     designation: authentication | ||||||
|     name: Welcome to authentik! |     name: Welcome to authentik! | ||||||
|     title: Welcome to authentik! |     title: Welcome to authentik! | ||||||
|  |     authentication: require_unauthenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-source-authentication |     slug: default-source-authentication | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: enrollment |     designation: enrollment | ||||||
|     name: Welcome to authentik! Please select a username. |     name: Welcome to authentik! Please select a username. | ||||||
|     title: Welcome to authentik! Please select a username. |     title: Welcome to authentik! Please select a username. | ||||||
|  |     authentication: none | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-source-enrollment |     slug: default-source-enrollment | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: stage_configuration |     designation: stage_configuration | ||||||
|     name: Pre-Authentication |     name: Pre-Authentication | ||||||
|     title: Pre-authentication |     title: Pre-authentication | ||||||
|  |     authentication: none | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-source-pre-authentication |     slug: default-source-pre-authentication | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ entries: | |||||||
|     designation: stage_configuration |     designation: stage_configuration | ||||||
|     name: User settings |     name: User settings | ||||||
|     title: Update your info |     title: Update your info | ||||||
|  |     authentication: require_authenticated | ||||||
|   identifiers: |   identifiers: | ||||||
|     slug: default-user-settings-flow |     slug: default-user-settings-flow | ||||||
|   model: authentik_flows.flow |   model: authentik_flows.flow | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ entries: | |||||||
|       name: Default enrollment Flow |       name: Default enrollment Flow | ||||||
|       title: Welcome to authentik! |       title: Welcome to authentik! | ||||||
|       designation: enrollment |       designation: enrollment | ||||||
|  |       authentication: require_unauthenticated | ||||||
|   - identifiers: |   - identifiers: | ||||||
|       field_key: username |       field_key: username | ||||||
|       label: Username |       label: Username | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ entries: | |||||||
|       name: Default enrollment Flow |       name: Default enrollment Flow | ||||||
|       title: Welcome to authentik! |       title: Welcome to authentik! | ||||||
|       designation: enrollment |       designation: enrollment | ||||||
|  |       authentication: require_unauthenticated | ||||||
|   - identifiers: |   - identifiers: | ||||||
|       field_key: username |       field_key: username | ||||||
|       label: Username |       label: Username | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ entries: | |||||||
|       name: Default Authentication Flow |       name: Default Authentication Flow | ||||||
|       title: Welcome to authentik! |       title: Welcome to authentik! | ||||||
|       designation: authentication |       designation: authentication | ||||||
|  |       authentication: require_unauthenticated | ||||||
|   - identifiers: |   - identifiers: | ||||||
|       name: test-not-app-password |       name: test-not-app-password | ||||||
|     id: test-not-app-password |     id: test-not-app-password | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ entries: | |||||||
|       name: Default Authentication Flow |       name: Default Authentication Flow | ||||||
|       title: Welcome to authentik! |       title: Welcome to authentik! | ||||||
|       designation: authentication |       designation: authentication | ||||||
|  |       authentication: require_unauthenticated | ||||||
|   - identifiers: |   - identifiers: | ||||||
|       name: default-authentication-login |       name: default-authentication-login | ||||||
|     id: default-authentication-login |     id: default-authentication-login | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ entries: | |||||||
|       name: Default recovery flow |       name: Default recovery flow | ||||||
|       title: Reset your password |       title: Reset your password | ||||||
|       designation: recovery |       designation: recovery | ||||||
|  |       authentication: require_unauthenticated | ||||||
|   - identifiers: |   - identifiers: | ||||||
|       field_key: password |       field_key: password | ||||||
|       label: Password |       label: Password | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ entries: | |||||||
|       name: Default unenrollment flow |       name: Default unenrollment flow | ||||||
|       title: Delete your account |       title: Delete your account | ||||||
|       designation: unenrollment |       designation: unenrollment | ||||||
|  |       authentication: require_authenticated | ||||||
|   - identifiers: |   - identifiers: | ||||||
|       name: default-unenrollment-user-delete |       name: default-unenrollment-user-delete | ||||||
|     id: default-unenrollment-user-delete |     id: default-unenrollment-user-delete | ||||||
|  | |||||||
| @ -121,6 +121,15 @@ | |||||||
|                     "id": { |                     "id": { | ||||||
|                         "type": "string" |                         "type": "string" | ||||||
|                     }, |                     }, | ||||||
|  |                     "state": { | ||||||
|  |                         "type": "string", | ||||||
|  |                         "enum": [ | ||||||
|  |                             "absent", | ||||||
|  |                             "present", | ||||||
|  |                             "created" | ||||||
|  |                         ], | ||||||
|  |                         "default": "present" | ||||||
|  |                     }, | ||||||
|                     "attrs": { |                     "attrs": { | ||||||
|                         "type": "object", |                         "type": "object", | ||||||
|                         "properties": { |                         "properties": { | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ services: | |||||||
|     volumes: |     volumes: | ||||||
|       - redis:/data |       - redis:/data | ||||||
|   server: |   server: | ||||||
|     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.11.0} |     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.11.2} | ||||||
|     restart: unless-stopped |     restart: unless-stopped | ||||||
|     command: server |     command: server | ||||||
|     environment: |     environment: | ||||||
| @ -52,7 +52,7 @@ services: | |||||||
|       - "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9000}:9000" |       - "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9000}:9000" | ||||||
|       - "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443" |       - "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443" | ||||||
|   worker: |   worker: | ||||||
|     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.11.0} |     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.11.2} | ||||||
|     restart: unless-stopped |     restart: unless-stopped | ||||||
|     command: worker |     command: worker | ||||||
|     environment: |     environment: | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -25,7 +25,7 @@ require ( | |||||||
| 	github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b | 	github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b | ||||||
| 	github.com/sirupsen/logrus v1.9.0 | 	github.com/sirupsen/logrus v1.9.0 | ||||||
| 	github.com/stretchr/testify v1.8.1 | 	github.com/stretchr/testify v1.8.1 | ||||||
| 	goauthentik.io/api/v3 v3.2022101.8 | 	goauthentik.io/api/v3 v3.2022110.1 | ||||||
| 	golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b | 	golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b | ||||||
| 	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f | 	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f | ||||||
| 	gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b | 	gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -376,8 +376,8 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | |||||||
| go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||||
| go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||||
| go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= | go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= | ||||||
| goauthentik.io/api/v3 v3.2022101.8 h1:s3dzv/4PQrvmmmxZ2Hte96CEmozk4vMHx7PSCzMi4Nw= | goauthentik.io/api/v3 v3.2022110.1 h1:CCfFjQ1Ah/M+6CXZASlE2v6ug98D4qOigJL5fq1Uboc= | ||||||
| goauthentik.io/api/v3 v3.2022101.8/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10= | goauthentik.io/api/v3 v3.2022110.1/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10= | ||||||
| golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= | golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= | ||||||
|  | |||||||
| @ -29,4 +29,4 @@ func UserAgent() string { | |||||||
| 	return fmt.Sprintf("authentik@%s", FullVersion()) | 	return fmt.Sprintf("authentik@%s", FullVersion()) | ||||||
| } | } | ||||||
|  |  | ||||||
| const VERSION = "2022.11.0" | const VERSION = "2022.11.2" | ||||||
|  | |||||||
| @ -100,7 +100,7 @@ addopts = "-p no:celery --junitxml=unittest.xml" | |||||||
|  |  | ||||||
| [tool.poetry] | [tool.poetry] | ||||||
| name = "authentik" | name = "authentik" | ||||||
| version = "2022.11.0" | version = "2022.11.2" | ||||||
| description = "" | description = "" | ||||||
| authors = ["authentik Team <hello@goauthentik.io>"] | authors = ["authentik Team <hello@goauthentik.io>"] | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										24
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								schema.yml
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
| openapi: 3.0.3 | openapi: 3.0.3 | ||||||
| info: | info: | ||||||
|   title: authentik |   title: authentik | ||||||
|   version: 2022.11.0 |   version: 2022.11.2 | ||||||
|   description: Making authentication simple. |   description: Making authentication simple. | ||||||
|   contact: |   contact: | ||||||
|     email: hello@goauthentik.io |     email: hello@goauthentik.io | ||||||
| @ -25269,6 +25269,13 @@ components: | |||||||
|       - last_used |       - last_used | ||||||
|       - user |       - user | ||||||
|       - user_agent |       - user_agent | ||||||
|  |     AuthenticationEnum: | ||||||
|  |       enum: | ||||||
|  |       - none | ||||||
|  |       - require_authenticated | ||||||
|  |       - require_unauthenticated | ||||||
|  |       - require_superuser | ||||||
|  |       type: string | ||||||
|     AuthenticatorAttachmentEnum: |     AuthenticatorAttachmentEnum: | ||||||
|       enum: |       enum: | ||||||
|       - platform |       - platform | ||||||
| @ -27578,6 +27585,11 @@ components: | |||||||
|           - $ref: '#/components/schemas/DeniedActionEnum' |           - $ref: '#/components/schemas/DeniedActionEnum' | ||||||
|           description: Configure what should happen when a flow denies access to a |           description: Configure what should happen when a flow denies access to a | ||||||
|             user. |             user. | ||||||
|  |         authentication: | ||||||
|  |           allOf: | ||||||
|  |           - $ref: '#/components/schemas/AuthenticationEnum' | ||||||
|  |           description: Required level of authentication and authorization to access | ||||||
|  |             a flow. | ||||||
|       required: |       required: | ||||||
|       - background |       - background | ||||||
|       - cache_count |       - cache_count | ||||||
| @ -27774,6 +27786,11 @@ components: | |||||||
|           - $ref: '#/components/schemas/DeniedActionEnum' |           - $ref: '#/components/schemas/DeniedActionEnum' | ||||||
|           description: Configure what should happen when a flow denies access to a |           description: Configure what should happen when a flow denies access to a | ||||||
|             user. |             user. | ||||||
|  |         authentication: | ||||||
|  |           allOf: | ||||||
|  |           - $ref: '#/components/schemas/AuthenticationEnum' | ||||||
|  |           description: Required level of authentication and authorization to access | ||||||
|  |             a flow. | ||||||
|       required: |       required: | ||||||
|       - designation |       - designation | ||||||
|       - name |       - name | ||||||
| @ -33651,6 +33668,11 @@ components: | |||||||
|           - $ref: '#/components/schemas/DeniedActionEnum' |           - $ref: '#/components/schemas/DeniedActionEnum' | ||||||
|           description: Configure what should happen when a flow denies access to a |           description: Configure what should happen when a flow denies access to a | ||||||
|             user. |             user. | ||||||
|  |         authentication: | ||||||
|  |           allOf: | ||||||
|  |           - $ref: '#/components/schemas/AuthenticationEnum' | ||||||
|  |           description: Required level of authentication and authorization to access | ||||||
|  |             a flow. | ||||||
|     PatchedFlowStageBindingRequest: |     PatchedFlowStageBindingRequest: | ||||||
|       type: object |       type: object | ||||||
|       description: FlowStageBinding Serializer |       description: FlowStageBinding Serializer | ||||||
|  | |||||||
							
								
								
									
										294
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										294
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -21,7 +21,7 @@ | |||||||
|                 "@codemirror/legacy-modes": "^6.3.0", |                 "@codemirror/legacy-modes": "^6.3.0", | ||||||
|                 "@formatjs/intl-listformat": "^7.1.3", |                 "@formatjs/intl-listformat": "^7.1.3", | ||||||
|                 "@fortawesome/fontawesome-free": "^6.2.1", |                 "@fortawesome/fontawesome-free": "^6.2.1", | ||||||
|                 "@goauthentik/api": "^2022.10.1-1669049898", |                 "@goauthentik/api": "^2022.11.0-1669065300", | ||||||
|                 "@jackfranklin/rollup-plugin-markdown": "^0.4.0", |                 "@jackfranklin/rollup-plugin-markdown": "^0.4.0", | ||||||
|                 "@lingui/cli": "^3.15.0", |                 "@lingui/cli": "^3.15.0", | ||||||
|                 "@lingui/core": "^3.15.0", |                 "@lingui/core": "^3.15.0", | ||||||
| @ -35,22 +35,22 @@ | |||||||
|                 "@rollup/plugin-node-resolve": "^15.0.1", |                 "@rollup/plugin-node-resolve": "^15.0.1", | ||||||
|                 "@rollup/plugin-replace": "^5.0.1", |                 "@rollup/plugin-replace": "^5.0.1", | ||||||
|                 "@rollup/plugin-typescript": "^9.0.2", |                 "@rollup/plugin-typescript": "^9.0.2", | ||||||
|                 "@sentry/browser": "^7.20.0", |                 "@sentry/browser": "^7.20.1", | ||||||
|                 "@sentry/tracing": "^7.20.0", |                 "@sentry/tracing": "^7.20.1", | ||||||
|                 "@squoosh/cli": "^0.7.2", |                 "@squoosh/cli": "^0.7.2", | ||||||
|                 "@trivago/prettier-plugin-sort-imports": "^3.4.0", |                 "@trivago/prettier-plugin-sort-imports": "^3.4.0", | ||||||
|                 "@types/chart.js": "^2.9.37", |                 "@types/chart.js": "^2.9.37", | ||||||
|                 "@types/codemirror": "5.60.5", |                 "@types/codemirror": "5.60.5", | ||||||
|                 "@types/grecaptcha": "^3.0.4", |                 "@types/grecaptcha": "^3.0.4", | ||||||
|                 "@types/mermaid": "^9.1.0", |                 "@types/mermaid": "^9.1.0", | ||||||
|                 "@typescript-eslint/eslint-plugin": "^5.43.0", |                 "@typescript-eslint/eslint-plugin": "^5.44.0", | ||||||
|                 "@typescript-eslint/parser": "^5.43.0", |                 "@typescript-eslint/parser": "^5.44.0", | ||||||
|                 "@webcomponents/webcomponentsjs": "^2.7.0", |                 "@webcomponents/webcomponentsjs": "^2.7.0", | ||||||
|                 "babel-plugin-macros": "^3.1.0", |                 "babel-plugin-macros": "^3.1.0", | ||||||
|                 "babel-plugin-tsconfig-paths": "^1.0.3", |                 "babel-plugin-tsconfig-paths": "^1.0.3", | ||||||
|                 "base64-js": "^1.5.1", |                 "base64-js": "^1.5.1", | ||||||
|                 "chart.js": "^3.9.1", |                 "chart.js": "^3.9.1", | ||||||
|                 "chartjs-adapter-moment": "^1.0.0", |                 "chartjs-adapter-moment": "^1.0.1", | ||||||
|                 "codemirror": "^6.0.1", |                 "codemirror": "^6.0.1", | ||||||
|                 "construct-style-sheets-polyfill": "^3.1.0", |                 "construct-style-sheets-polyfill": "^3.1.0", | ||||||
|                 "country-flag-icons": "^1.5.5", |                 "country-flag-icons": "^1.5.5", | ||||||
| @ -1957,9 +1957,9 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@goauthentik/api": { |         "node_modules/@goauthentik/api": { | ||||||
|             "version": "2022.10.1-1669049898", |             "version": "2022.11.0-1669065300", | ||||||
|             "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.10.1-1669049898.tgz", |             "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.11.0-1669065300.tgz", | ||||||
|             "integrity": "sha512-HzPCZ9HDZNCGk3wU/n/Z8jJ0KuRuWslxTL/Dq5n7vEPb2GYf6EvVQLGIIBPuWq4we9QpPRmgXJRtUYHjCxiSuw==" |             "integrity": "sha512-6/8XZlexxzum6ZpO8/Q8rTA0P52F7DU9ECDTMMU+D5TKGZbsatUdly61XdjOtMLN5vE0x3i7GaMreLgRUVEDKA==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/@humanwhocodes/config-array": { |         "node_modules/@humanwhocodes/config-array": { | ||||||
|             "version": "0.11.6", |             "version": "0.11.6", | ||||||
| @ -2944,13 +2944,13 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@sentry/browser": { |         "node_modules/@sentry/browser": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.20.1.tgz", | ||||||
|             "integrity": "sha512-L84CdB7DPQ2ohVcWh/KivemndWSZyXRvBZBr+tHFlQchzcaZZ/8lIPvjwvb8OJhzhecDq6JCAyUxaZwyItdyAg==", |             "integrity": "sha512-SE6mI4LkMzjEi5KB02Py24e2bKYZc/HZI/ZlTn36BuUQX/KYhzzKwzXucOJ5Qws9Ar9CViyKJDb07LxVQLYCGw==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/core": "7.20.0", |                 "@sentry/core": "7.20.1", | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "@sentry/utils": "7.20.0", |                 "@sentry/utils": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
| @ -2963,12 +2963,12 @@ | |||||||
|             "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" |             "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/@sentry/core": { |         "node_modules/@sentry/core": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.20.1.tgz", | ||||||
|             "integrity": "sha512-8dIHk8niyEyVayUQpgECHnV2p444nPBjIyuQrtkdTxL7sBLC5+Y0DhRjxg9cJyZe/bZnXVerGkgcA7niKW4W8A==", |             "integrity": "sha512-Sc7vtNgO4QcE683qrR+b+KFQkkhvQv7gizN46QQPOWeqLDrai7x0+NspTFDLJyvdDuDh2rjoLfRwNsgbwe7Erw==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "@sentry/utils": "7.20.0", |                 "@sentry/utils": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
| @ -2981,13 +2981,13 @@ | |||||||
|             "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" |             "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/@sentry/tracing": { |         "node_modules/@sentry/tracing": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.20.1.tgz", | ||||||
|             "integrity": "sha512-qg3sMvjuMQl/NEaF8I2IpvUcJ4HGGVIwEqqqZ6hkeHXIKt02p6f+nls45pVhluMiNHAaQJ+vefMTUc3E1yZwDA==", |             "integrity": "sha512-LAiQcJMcOFkUwkGvqLghcVOtVVglHBQ2r7kRo75kqI0OTn/xMPRyPBGo94G+9zAKm+w7dGF5AUqq/4VUm7DJ+g==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/core": "7.20.0", |                 "@sentry/core": "7.20.1", | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "@sentry/utils": "7.20.0", |                 "@sentry/utils": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
| @ -3000,19 +3000,19 @@ | |||||||
|             "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" |             "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/@sentry/types": { |         "node_modules/@sentry/types": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.20.1.tgz", | ||||||
|             "integrity": "sha512-x17ddduGWqW95neBFVvxzmInb5WXVw+2PcNASHXpGFhi7v2gz2a7/w2CcIKxsqODNnc+z/k1t0Y+uy9B6aH6ag==", |             "integrity": "sha512-bI4t5IXGLIQYH5MegKRl4x2LDSlPVbQJ5eE6NJCMrCm8PcFUo3WgkwP6toG9ThQwpTx/DhUo1sVNKrr0oW4cpA==", | ||||||
|             "engines": { |             "engines": { | ||||||
|                 "node": ">=8" |                 "node": ">=8" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@sentry/utils": { |         "node_modules/@sentry/utils": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.20.1.tgz", | ||||||
|             "integrity": "sha512-4lc122TFgkaCAvoPRy+uc5vgOCumTa/2nPkzCSxVsezQs+ebHxyMJQK7GWBLI6P+EzKfEjlgyMzRWaPJ3iJatA==", |             "integrity": "sha512-wToW0710OijQLUZnbbOx1pxwJ4mXUZ5ZFl4/x7ubNftkOz5NwJ+F3ylRqHXpZJaR9pUfR5CNdInTFZn05h/KeQ==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
| @ -3365,13 +3365,13 @@ | |||||||
|             "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" |             "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/eslint-plugin": { |         "node_modules/@typescript-eslint/eslint-plugin": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz", | ||||||
|             "integrity": "sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==", |             "integrity": "sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@typescript-eslint/scope-manager": "5.43.0", |                 "@typescript-eslint/scope-manager": "5.44.0", | ||||||
|                 "@typescript-eslint/type-utils": "5.43.0", |                 "@typescript-eslint/type-utils": "5.44.0", | ||||||
|                 "@typescript-eslint/utils": "5.43.0", |                 "@typescript-eslint/utils": "5.44.0", | ||||||
|                 "debug": "^4.3.4", |                 "debug": "^4.3.4", | ||||||
|                 "ignore": "^5.2.0", |                 "ignore": "^5.2.0", | ||||||
|                 "natural-compare-lite": "^1.4.0", |                 "natural-compare-lite": "^1.4.0", | ||||||
| @ -3411,13 +3411,13 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/parser": { |         "node_modules/@typescript-eslint/parser": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", | ||||||
|             "integrity": "sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==", |             "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@typescript-eslint/scope-manager": "5.43.0", |                 "@typescript-eslint/scope-manager": "5.44.0", | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/typescript-estree": "5.43.0", |                 "@typescript-eslint/typescript-estree": "5.44.0", | ||||||
|                 "debug": "^4.3.4" |                 "debug": "^4.3.4" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
| @ -3437,12 +3437,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/scope-manager": { |         "node_modules/@typescript-eslint/scope-manager": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz", | ||||||
|             "integrity": "sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw==", |             "integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/visitor-keys": "5.43.0" |                 "@typescript-eslint/visitor-keys": "5.44.0" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
|                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0" |                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | ||||||
| @ -3453,12 +3453,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/type-utils": { |         "node_modules/@typescript-eslint/type-utils": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz", | ||||||
|             "integrity": "sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==", |             "integrity": "sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@typescript-eslint/typescript-estree": "5.43.0", |                 "@typescript-eslint/typescript-estree": "5.44.0", | ||||||
|                 "@typescript-eslint/utils": "5.43.0", |                 "@typescript-eslint/utils": "5.44.0", | ||||||
|                 "debug": "^4.3.4", |                 "debug": "^4.3.4", | ||||||
|                 "tsutils": "^3.21.0" |                 "tsutils": "^3.21.0" | ||||||
|             }, |             }, | ||||||
| @ -3479,9 +3479,9 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/types": { |         "node_modules/@typescript-eslint/types": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz", | ||||||
|             "integrity": "sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==", |             "integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==", | ||||||
|             "engines": { |             "engines": { | ||||||
|                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0" |                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | ||||||
|             }, |             }, | ||||||
| @ -3491,12 +3491,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/typescript-estree": { |         "node_modules/@typescript-eslint/typescript-estree": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz", | ||||||
|             "integrity": "sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==", |             "integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/visitor-keys": "5.43.0", |                 "@typescript-eslint/visitor-keys": "5.44.0", | ||||||
|                 "debug": "^4.3.4", |                 "debug": "^4.3.4", | ||||||
|                 "globby": "^11.1.0", |                 "globby": "^11.1.0", | ||||||
|                 "is-glob": "^4.0.3", |                 "is-glob": "^4.0.3", | ||||||
| @ -3531,15 +3531,15 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/utils": { |         "node_modules/@typescript-eslint/utils": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.44.0.tgz", | ||||||
|             "integrity": "sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==", |             "integrity": "sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@types/json-schema": "^7.0.9", |                 "@types/json-schema": "^7.0.9", | ||||||
|                 "@types/semver": "^7.3.12", |                 "@types/semver": "^7.3.12", | ||||||
|                 "@typescript-eslint/scope-manager": "5.43.0", |                 "@typescript-eslint/scope-manager": "5.44.0", | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/typescript-estree": "5.43.0", |                 "@typescript-eslint/typescript-estree": "5.44.0", | ||||||
|                 "eslint-scope": "^5.1.1", |                 "eslint-scope": "^5.1.1", | ||||||
|                 "eslint-utils": "^3.0.0", |                 "eslint-utils": "^3.0.0", | ||||||
|                 "semver": "^7.3.7" |                 "semver": "^7.3.7" | ||||||
| @ -3570,11 +3570,11 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "node_modules/@typescript-eslint/visitor-keys": { |         "node_modules/@typescript-eslint/visitor-keys": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz", | ||||||
|             "integrity": "sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg==", |             "integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==", | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "eslint-visitor-keys": "^3.3.0" |                 "eslint-visitor-keys": "^3.3.0" | ||||||
|             }, |             }, | ||||||
|             "engines": { |             "engines": { | ||||||
| @ -4185,11 +4185,11 @@ | |||||||
|             "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" |             "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" | ||||||
|         }, |         }, | ||||||
|         "node_modules/chartjs-adapter-moment": { |         "node_modules/chartjs-adapter-moment": { | ||||||
|             "version": "1.0.0", |             "version": "1.0.1", | ||||||
|             "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.0.tgz", |             "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz", | ||||||
|             "integrity": "sha512-PqlerEvQcc5hZLQ/NQWgBxgVQ4TRdvkW3c/t+SUEQSj78ia3hgLkf2VZ2yGJtltNbEEFyYGm+cA6XXevodYvWA==", |             "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==", | ||||||
|             "peerDependencies": { |             "peerDependencies": { | ||||||
|                 "chart.js": "^3.0.0", |                 "chart.js": ">=3.0.0", | ||||||
|                 "moment": "^2.10.2" |                 "moment": "^2.10.2" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
| @ -11667,9 +11667,9 @@ | |||||||
|             "integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A==" |             "integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A==" | ||||||
|         }, |         }, | ||||||
|         "@goauthentik/api": { |         "@goauthentik/api": { | ||||||
|             "version": "2022.10.1-1669049898", |             "version": "2022.11.0-1669065300", | ||||||
|             "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.10.1-1669049898.tgz", |             "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2022.11.0-1669065300.tgz", | ||||||
|             "integrity": "sha512-HzPCZ9HDZNCGk3wU/n/Z8jJ0KuRuWslxTL/Dq5n7vEPb2GYf6EvVQLGIIBPuWq4we9QpPRmgXJRtUYHjCxiSuw==" |             "integrity": "sha512-6/8XZlexxzum6ZpO8/Q8rTA0P52F7DU9ECDTMMU+D5TKGZbsatUdly61XdjOtMLN5vE0x3i7GaMreLgRUVEDKA==" | ||||||
|         }, |         }, | ||||||
|         "@humanwhocodes/config-array": { |         "@humanwhocodes/config-array": { | ||||||
|             "version": "0.11.6", |             "version": "0.11.6", | ||||||
| @ -12398,13 +12398,13 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/browser": { |         "@sentry/browser": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.20.1.tgz", | ||||||
|             "integrity": "sha512-L84CdB7DPQ2ohVcWh/KivemndWSZyXRvBZBr+tHFlQchzcaZZ/8lIPvjwvb8OJhzhecDq6JCAyUxaZwyItdyAg==", |             "integrity": "sha512-SE6mI4LkMzjEi5KB02Py24e2bKYZc/HZI/ZlTn36BuUQX/KYhzzKwzXucOJ5Qws9Ar9CViyKJDb07LxVQLYCGw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/core": "7.20.0", |                 "@sentry/core": "7.20.1", | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "@sentry/utils": "7.20.0", |                 "@sentry/utils": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -12416,12 +12416,12 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/core": { |         "@sentry/core": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.20.1.tgz", | ||||||
|             "integrity": "sha512-8dIHk8niyEyVayUQpgECHnV2p444nPBjIyuQrtkdTxL7sBLC5+Y0DhRjxg9cJyZe/bZnXVerGkgcA7niKW4W8A==", |             "integrity": "sha512-Sc7vtNgO4QcE683qrR+b+KFQkkhvQv7gizN46QQPOWeqLDrai7x0+NspTFDLJyvdDuDh2rjoLfRwNsgbwe7Erw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "@sentry/utils": "7.20.0", |                 "@sentry/utils": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -12433,13 +12433,13 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/tracing": { |         "@sentry/tracing": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.20.1.tgz", | ||||||
|             "integrity": "sha512-qg3sMvjuMQl/NEaF8I2IpvUcJ4HGGVIwEqqqZ6hkeHXIKt02p6f+nls45pVhluMiNHAaQJ+vefMTUc3E1yZwDA==", |             "integrity": "sha512-LAiQcJMcOFkUwkGvqLghcVOtVVglHBQ2r7kRo75kqI0OTn/xMPRyPBGo94G+9zAKm+w7dGF5AUqq/4VUm7DJ+g==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/core": "7.20.0", |                 "@sentry/core": "7.20.1", | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "@sentry/utils": "7.20.0", |                 "@sentry/utils": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -12451,16 +12451,16 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@sentry/types": { |         "@sentry/types": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.20.1.tgz", | ||||||
|             "integrity": "sha512-x17ddduGWqW95neBFVvxzmInb5WXVw+2PcNASHXpGFhi7v2gz2a7/w2CcIKxsqODNnc+z/k1t0Y+uy9B6aH6ag==" |             "integrity": "sha512-bI4t5IXGLIQYH5MegKRl4x2LDSlPVbQJ5eE6NJCMrCm8PcFUo3WgkwP6toG9ThQwpTx/DhUo1sVNKrr0oW4cpA==" | ||||||
|         }, |         }, | ||||||
|         "@sentry/utils": { |         "@sentry/utils": { | ||||||
|             "version": "7.20.0", |             "version": "7.20.1", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.20.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.20.1.tgz", | ||||||
|             "integrity": "sha512-4lc122TFgkaCAvoPRy+uc5vgOCumTa/2nPkzCSxVsezQs+ebHxyMJQK7GWBLI6P+EzKfEjlgyMzRWaPJ3iJatA==", |             "integrity": "sha512-wToW0710OijQLUZnbbOx1pxwJ4mXUZ5ZFl4/x7ubNftkOz5NwJ+F3ylRqHXpZJaR9pUfR5CNdInTFZn05h/KeQ==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@sentry/types": "7.20.0", |                 "@sentry/types": "7.20.1", | ||||||
|                 "tslib": "^1.9.3" |                 "tslib": "^1.9.3" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -12776,13 +12776,13 @@ | |||||||
|             "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" |             "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/eslint-plugin": { |         "@typescript-eslint/eslint-plugin": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz", | ||||||
|             "integrity": "sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==", |             "integrity": "sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/scope-manager": "5.43.0", |                 "@typescript-eslint/scope-manager": "5.44.0", | ||||||
|                 "@typescript-eslint/type-utils": "5.43.0", |                 "@typescript-eslint/type-utils": "5.44.0", | ||||||
|                 "@typescript-eslint/utils": "5.43.0", |                 "@typescript-eslint/utils": "5.44.0", | ||||||
|                 "debug": "^4.3.4", |                 "debug": "^4.3.4", | ||||||
|                 "ignore": "^5.2.0", |                 "ignore": "^5.2.0", | ||||||
|                 "natural-compare-lite": "^1.4.0", |                 "natural-compare-lite": "^1.4.0", | ||||||
| @ -12802,48 +12802,48 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/parser": { |         "@typescript-eslint/parser": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", | ||||||
|             "integrity": "sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==", |             "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/scope-manager": "5.43.0", |                 "@typescript-eslint/scope-manager": "5.44.0", | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/typescript-estree": "5.43.0", |                 "@typescript-eslint/typescript-estree": "5.44.0", | ||||||
|                 "debug": "^4.3.4" |                 "debug": "^4.3.4" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/scope-manager": { |         "@typescript-eslint/scope-manager": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz", | ||||||
|             "integrity": "sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw==", |             "integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/visitor-keys": "5.43.0" |                 "@typescript-eslint/visitor-keys": "5.44.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/type-utils": { |         "@typescript-eslint/type-utils": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz", | ||||||
|             "integrity": "sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==", |             "integrity": "sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/typescript-estree": "5.43.0", |                 "@typescript-eslint/typescript-estree": "5.44.0", | ||||||
|                 "@typescript-eslint/utils": "5.43.0", |                 "@typescript-eslint/utils": "5.44.0", | ||||||
|                 "debug": "^4.3.4", |                 "debug": "^4.3.4", | ||||||
|                 "tsutils": "^3.21.0" |                 "tsutils": "^3.21.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/types": { |         "@typescript-eslint/types": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz", | ||||||
|             "integrity": "sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==" |             "integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==" | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/typescript-estree": { |         "@typescript-eslint/typescript-estree": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz", | ||||||
|             "integrity": "sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==", |             "integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/visitor-keys": "5.43.0", |                 "@typescript-eslint/visitor-keys": "5.44.0", | ||||||
|                 "debug": "^4.3.4", |                 "debug": "^4.3.4", | ||||||
|                 "globby": "^11.1.0", |                 "globby": "^11.1.0", | ||||||
|                 "is-glob": "^4.0.3", |                 "is-glob": "^4.0.3", | ||||||
| @ -12862,15 +12862,15 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/utils": { |         "@typescript-eslint/utils": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.44.0.tgz", | ||||||
|             "integrity": "sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==", |             "integrity": "sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@types/json-schema": "^7.0.9", |                 "@types/json-schema": "^7.0.9", | ||||||
|                 "@types/semver": "^7.3.12", |                 "@types/semver": "^7.3.12", | ||||||
|                 "@typescript-eslint/scope-manager": "5.43.0", |                 "@typescript-eslint/scope-manager": "5.44.0", | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "@typescript-eslint/typescript-estree": "5.43.0", |                 "@typescript-eslint/typescript-estree": "5.44.0", | ||||||
|                 "eslint-scope": "^5.1.1", |                 "eslint-scope": "^5.1.1", | ||||||
|                 "eslint-utils": "^3.0.0", |                 "eslint-utils": "^3.0.0", | ||||||
|                 "semver": "^7.3.7" |                 "semver": "^7.3.7" | ||||||
| @ -12887,11 +12887,11 @@ | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "@typescript-eslint/visitor-keys": { |         "@typescript-eslint/visitor-keys": { | ||||||
|             "version": "5.43.0", |             "version": "5.44.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz", |             "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz", | ||||||
|             "integrity": "sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg==", |             "integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==", | ||||||
|             "requires": { |             "requires": { | ||||||
|                 "@typescript-eslint/types": "5.43.0", |                 "@typescript-eslint/types": "5.44.0", | ||||||
|                 "eslint-visitor-keys": "^3.3.0" |                 "eslint-visitor-keys": "^3.3.0" | ||||||
|             }, |             }, | ||||||
|             "dependencies": { |             "dependencies": { | ||||||
| @ -13325,9 +13325,9 @@ | |||||||
|             "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" |             "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" | ||||||
|         }, |         }, | ||||||
|         "chartjs-adapter-moment": { |         "chartjs-adapter-moment": { | ||||||
|             "version": "1.0.0", |             "version": "1.0.1", | ||||||
|             "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.0.tgz", |             "resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz", | ||||||
|             "integrity": "sha512-PqlerEvQcc5hZLQ/NQWgBxgVQ4TRdvkW3c/t+SUEQSj78ia3hgLkf2VZ2yGJtltNbEEFyYGm+cA6XXevodYvWA==", |             "integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==", | ||||||
|             "requires": {} |             "requires": {} | ||||||
|         }, |         }, | ||||||
|         "chokidar": { |         "chokidar": { | ||||||
|  | |||||||
| @ -64,7 +64,7 @@ | |||||||
|         "@codemirror/legacy-modes": "^6.3.0", |         "@codemirror/legacy-modes": "^6.3.0", | ||||||
|         "@formatjs/intl-listformat": "^7.1.3", |         "@formatjs/intl-listformat": "^7.1.3", | ||||||
|         "@fortawesome/fontawesome-free": "^6.2.1", |         "@fortawesome/fontawesome-free": "^6.2.1", | ||||||
|         "@goauthentik/api": "^2022.10.1-1669049898", |         "@goauthentik/api": "^2022.11.0-1669065300", | ||||||
|         "@jackfranklin/rollup-plugin-markdown": "^0.4.0", |         "@jackfranklin/rollup-plugin-markdown": "^0.4.0", | ||||||
|         "@lingui/cli": "^3.15.0", |         "@lingui/cli": "^3.15.0", | ||||||
|         "@lingui/core": "^3.15.0", |         "@lingui/core": "^3.15.0", | ||||||
| @ -78,22 +78,22 @@ | |||||||
|         "@rollup/plugin-node-resolve": "^15.0.1", |         "@rollup/plugin-node-resolve": "^15.0.1", | ||||||
|         "@rollup/plugin-replace": "^5.0.1", |         "@rollup/plugin-replace": "^5.0.1", | ||||||
|         "@rollup/plugin-typescript": "^9.0.2", |         "@rollup/plugin-typescript": "^9.0.2", | ||||||
|         "@sentry/browser": "^7.20.0", |         "@sentry/browser": "^7.20.1", | ||||||
|         "@sentry/tracing": "^7.20.0", |         "@sentry/tracing": "^7.20.1", | ||||||
|         "@squoosh/cli": "^0.7.2", |         "@squoosh/cli": "^0.7.2", | ||||||
|         "@trivago/prettier-plugin-sort-imports": "^3.4.0", |         "@trivago/prettier-plugin-sort-imports": "^3.4.0", | ||||||
|         "@types/chart.js": "^2.9.37", |         "@types/chart.js": "^2.9.37", | ||||||
|         "@types/codemirror": "5.60.5", |         "@types/codemirror": "5.60.5", | ||||||
|         "@types/grecaptcha": "^3.0.4", |         "@types/grecaptcha": "^3.0.4", | ||||||
|         "@types/mermaid": "^9.1.0", |         "@types/mermaid": "^9.1.0", | ||||||
|         "@typescript-eslint/eslint-plugin": "^5.43.0", |         "@typescript-eslint/eslint-plugin": "^5.44.0", | ||||||
|         "@typescript-eslint/parser": "^5.43.0", |         "@typescript-eslint/parser": "^5.44.0", | ||||||
|         "@webcomponents/webcomponentsjs": "^2.7.0", |         "@webcomponents/webcomponentsjs": "^2.7.0", | ||||||
|         "babel-plugin-macros": "^3.1.0", |         "babel-plugin-macros": "^3.1.0", | ||||||
|         "babel-plugin-tsconfig-paths": "^1.0.3", |         "babel-plugin-tsconfig-paths": "^1.0.3", | ||||||
|         "base64-js": "^1.5.1", |         "base64-js": "^1.5.1", | ||||||
|         "chart.js": "^3.9.1", |         "chart.js": "^3.9.1", | ||||||
|         "chartjs-adapter-moment": "^1.0.0", |         "chartjs-adapter-moment": "^1.0.1", | ||||||
|         "codemirror": "^6.0.1", |         "codemirror": "^6.0.1", | ||||||
|         "construct-style-sheets-polyfill": "^3.1.0", |         "construct-style-sheets-polyfill": "^3.1.0", | ||||||
|         "country-flag-icons": "^1.5.5", |         "country-flag-icons": "^1.5.5", | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| Contact: mailto:security@goauthentik.io | Contact: mailto:security@goauthentik.io | ||||||
| Expires: Sat, 1 Jan 2023 00:00 +0200 | Expires: Mon, 1 Jan 2024 00:00 +0200 | ||||||
| Preferred-Languages: en, de | Preferred-Languages: en, de | ||||||
| Policy: https://github.com/goauthentik/authentik/blob/main/SECURITY.md | Policy: https://github.com/goauthentik/authentik/blob/main/SECURITY.md | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils"; | import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils"; | ||||||
|  | import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum"; | ||||||
| import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config"; | ||||||
| import { first } from "@goauthentik/common/utils"; | import { first } from "@goauthentik/common/utils"; | ||||||
| import "@goauthentik/elements/forms/HorizontalFormElement"; | import "@goauthentik/elements/forms/HorizontalFormElement"; | ||||||
| @ -141,6 +142,37 @@ export class FlowForm extends ModelForm<Flow, string> { | |||||||
|             </option>`; |             </option>`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     renderAuthentication(): TemplateResult { | ||||||
|  |         return html` | ||||||
|  |             <option | ||||||
|  |                 value=${AuthenticationEnum.None} | ||||||
|  |                 ?selected=${this.instance?.authentication === AuthenticationEnum.None} | ||||||
|  |             > | ||||||
|  |                 ${t`No requirement`} | ||||||
|  |             </option> | ||||||
|  |             <option | ||||||
|  |                 value=${AuthenticationEnum.RequireAuthenticated} | ||||||
|  |                 ?selected=${this.instance?.authentication === | ||||||
|  |                 AuthenticationEnum.RequireAuthenticated} | ||||||
|  |             > | ||||||
|  |                 ${t`Require authentication`} | ||||||
|  |             </option> | ||||||
|  |             <option | ||||||
|  |                 value=${AuthenticationEnum.RequireUnauthenticated} | ||||||
|  |                 ?selected=${this.instance?.authentication === | ||||||
|  |                 AuthenticationEnum.RequireUnauthenticated} | ||||||
|  |             > | ||||||
|  |                 ${t`Require no authentication.`} | ||||||
|  |             </option> | ||||||
|  |             <option | ||||||
|  |                 value=${AuthenticationEnum.RequireSuperuser} | ||||||
|  |                 ?selected=${this.instance?.authentication === AuthenticationEnum.RequireSuperuser} | ||||||
|  |             > | ||||||
|  |                 ${t`Require superuser.`} | ||||||
|  |             </option> | ||||||
|  |         `; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     renderLayout(): TemplateResult { |     renderLayout(): TemplateResult { | ||||||
|         return html` |         return html` | ||||||
|             <option |             <option | ||||||
| @ -224,6 +256,18 @@ export class FlowForm extends ModelForm<Flow, string> { | |||||||
|                     </option> |                     </option> | ||||||
|                 </select> |                 </select> | ||||||
|             </ak-form-element-horizontal> |             </ak-form-element-horizontal> | ||||||
|  |             <ak-form-element-horizontal | ||||||
|  |                 label=${t`Authentication`} | ||||||
|  |                 ?required=${true} | ||||||
|  |                 name="authentication" | ||||||
|  |             > | ||||||
|  |                 <select class="pf-c-form-control"> | ||||||
|  |                     ${this.renderAuthentication()} | ||||||
|  |                 </select> | ||||||
|  |                 <p class="pf-c-form__helper-text"> | ||||||
|  |                     ${t`Required authentication level for this flow.`} | ||||||
|  |                 </p> | ||||||
|  |             </ak-form-element-horizontal> | ||||||
|             <ak-form-element-horizontal |             <ak-form-element-horizontal | ||||||
|                 label=${t`Designation`} |                 label=${t`Designation`} | ||||||
|                 ?required=${true} |                 ?required=${true} | ||||||
|  | |||||||
| @ -72,6 +72,9 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> { | |||||||
|  |  | ||||||
|     send = (data: ProxyProvider): Promise<ProxyProvider> => { |     send = (data: ProxyProvider): Promise<ProxyProvider> => { | ||||||
|         data.mode = this.mode; |         data.mode = this.mode; | ||||||
|  |         if (this.mode !== ProxyMode.ForwardDomain) { | ||||||
|  |             data.cookieDomain = ""; | ||||||
|  |         } | ||||||
|         if (this.instance) { |         if (this.instance) { | ||||||
|             return new ProvidersApi(DEFAULT_CONFIG).providersProxyUpdate({ |             return new ProvidersApi(DEFAULT_CONFIG).providersProxyUpdate({ | ||||||
|                 id: this.instance.pk || 0, |                 id: this.instance.pk || 0, | ||||||
|  | |||||||
| @ -16,9 +16,9 @@ import { until } from "lit/directives/until.js"; | |||||||
|  |  | ||||||
| import { | import { | ||||||
|     AuthenticatorDuoStage, |     AuthenticatorDuoStage, | ||||||
|  |     AuthenticatorDuoStageManualDeviceImportRequest, | ||||||
|     CoreApi, |     CoreApi, | ||||||
|     StagesApi, |     StagesApi, | ||||||
|     StagesAuthenticatorDuoImportDeviceManualCreateRequest, |  | ||||||
| } from "@goauthentik/api"; | } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| @customElement("ak-stage-authenticator-duo-device-import-form") | @customElement("ak-stage-authenticator-duo-device-import-form") | ||||||
| @ -34,11 +34,11 @@ export class DuoDeviceImportForm extends ModelForm<AuthenticatorDuoStage, string | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     send = (data: AuthenticatorDuoStage): Promise<void> => { |     send = (data: AuthenticatorDuoStage): Promise<void> => { | ||||||
|         const importData = data as unknown as StagesAuthenticatorDuoImportDeviceManualCreateRequest; |         const importData = data as unknown as AuthenticatorDuoStageManualDeviceImportRequest; | ||||||
|         importData.stageUuid = this.instancePk; |         return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoImportDeviceManualCreate({ | ||||||
|         return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoImportDeviceManualCreate( |             stageUuid: this.instance?.pk || "", | ||||||
|             importData, |             authenticatorDuoStageManualDeviceImportRequest: importData, | ||||||
|         ); |         }); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     renderForm(): TemplateResult { |     renderForm(): TemplateResult { | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { first } from "@goauthentik/common/utils"; |  | ||||||
| import "@goauthentik/elements/forms/FormGroup"; | import "@goauthentik/elements/forms/FormGroup"; | ||||||
| import "@goauthentik/elements/forms/HorizontalFormElement"; | import "@goauthentik/elements/forms/HorizontalFormElement"; | ||||||
| import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; | import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success"; | |||||||
| export const ERROR_CLASS = "pf-m-danger"; | export const ERROR_CLASS = "pf-m-danger"; | ||||||
| export const PROGRESS_CLASS = "pf-m-in-progress"; | export const PROGRESS_CLASS = "pf-m-in-progress"; | ||||||
| export const CURRENT_CLASS = "pf-m-current"; | export const CURRENT_CLASS = "pf-m-current"; | ||||||
| export const VERSION = "2022.11.0"; | export const VERSION = "2022.11.2"; | ||||||
| export const TITLE_DEFAULT = "authentik"; | export const TITLE_DEFAULT = "authentik"; | ||||||
| export const ROUTE_SEPARATOR = ";"; | export const ROUTE_SEPARATOR = ";"; | ||||||
|  |  | ||||||
|  | |||||||
| @ -24,4 +24,4 @@ This export can be triggered via the API or the Web UI by clicking the download | |||||||
|  |  | ||||||
| ## Cleaning up | ## Cleaning up | ||||||
|  |  | ||||||
| Exports from either method will contain a (potentially) long list of objects, all with hardcoded primary keys and now ability for templating/instantiation. This is because currently, authentik does not check which primary keys are used where. It is assumed that for most exports, there'll be some manual changes done regardless, to filter out unwanted objects, adjust properties, etc. | Exports from either method will contain a (potentially) long list of objects, all with hardcoded primary keys and no ability for templating/instantiation. This is because currently, authentik does not check which primary keys are used where. It is assumed that for most exports, there'll be some manual changes done regardless, to filter out unwanted objects, adjust properties, etc. | ||||||
|  | |||||||
| @ -20,6 +20,11 @@ context: | |||||||
| entries: | entries: | ||||||
|     - # Model in app.model notation, possibilities are listed in the schema (required) |     - # Model in app.model notation, possibilities are listed in the schema (required) | ||||||
|       model: authentik_flows.flow |       model: authentik_flows.flow | ||||||
|  |       # The state this object should be in (optional, can be "present", "created" or "absent") | ||||||
|  |       # Present will keep the object in sync with its definition here, created will only ensure | ||||||
|  |       # the object is created (and create it with the values given here), and "absent" will | ||||||
|  |       # delete the object | ||||||
|  |       state: present | ||||||
|       # Key:value filters to uniquely identify this object (required) |       # Key:value filters to uniquely identify this object (required) | ||||||
|       identifiers: |       identifiers: | ||||||
|           slug: initial-setup |           slug: initial-setup | ||||||
|  | |||||||
| @ -62,6 +62,15 @@ image: | |||||||
| -   web: fix twitter icon | -   web: fix twitter icon | ||||||
| -   web/flows: always hide static user info when its not set in the flow | -   web/flows: always hide static user info when its not set in the flow | ||||||
|  |  | ||||||
|  | ## Fixed in 2022.11.1 | ||||||
|  |  | ||||||
|  | -   blueprints: add desired state attribute to objects (#4061) | ||||||
|  | -   core: fix tab-complete in shell | ||||||
|  | -   root: fix build on arm64 | ||||||
|  | -   stages/email: add test for email translation | ||||||
|  | -   web/admin: fix error when importing duo devices | ||||||
|  | -   web/admin: reset cookie_domain when setting non-domain forward auth | ||||||
|  |  | ||||||
| ## API Changes | ## API Changes | ||||||
|  |  | ||||||
| #### What's Changed | #### What's Changed | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								website/docs/security/CVE-2022-46145.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								website/docs/security/CVE-2022-46145.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | # CVE-2022-46145 | ||||||
|  |  | ||||||
|  | ## Unauthorized user creation and potential account takeover | ||||||
|  |  | ||||||
|  | ### Impact | ||||||
|  |  | ||||||
|  | With the default flows, unauthenticated users can create new accounts in authentik. If a flow exists that allows for email-verified password recovery, this can be used to overwrite the email address of admin accounts and take over their accounts | ||||||
|  |  | ||||||
|  | ### Patches | ||||||
|  |  | ||||||
|  | authentik 2022.11.2 and 2022.10.2 fix this issue, for other versions the workaround can be used. | ||||||
|  |  | ||||||
|  | ### Workarounds | ||||||
|  |  | ||||||
|  | A policy can be created and bound to the `default-user-settings-flow` flow with the following contents | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | return request.user.is_authenticated | ||||||
|  | ``` | ||||||
							
								
								
									
										5
									
								
								website/docs/security/policy.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								website/docs/security/policy.mdx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | # Security Policy | ||||||
|  |  | ||||||
|  | import SecurityPolicy from "../../../SECURITY.md"; | ||||||
|  |  | ||||||
|  | <SecurityPolicy /> | ||||||
| @ -95,7 +95,7 @@ module.exports = { | |||||||
|                         }, |                         }, | ||||||
|                         { |                         { | ||||||
|                             label: "Installations", |                             label: "Installations", | ||||||
|                             to: "docs/installation/index", |                             to: "docs/installation/", | ||||||
|                         }, |                         }, | ||||||
|                     ], |                     ], | ||||||
|                 }, |                 }, | ||||||
|  | |||||||
| @ -282,5 +282,15 @@ module.exports = { | |||||||
|                 "troubleshooting/missing_admin_group", |                 "troubleshooting/missing_admin_group", | ||||||
|             ], |             ], | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             type: "category", | ||||||
|  |             label: "Security", | ||||||
|  |             link: { | ||||||
|  |                 type: "generated-index", | ||||||
|  |                 title: "Security", | ||||||
|  |                 slug: "security", | ||||||
|  |             }, | ||||||
|  |             items: ["security/policy", "security/CVE-2022-46145"], | ||||||
|  |         }, | ||||||
|     ], |     ], | ||||||
| }; | }; | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	