Compare commits

...

40 Commits

Author SHA1 Message Date
3fa987f443 start config file watch
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-25 21:32:33 +01:00
5ea9595c9c internal: fix cache-control header
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

#4525
2023-01-25 21:18:20 +01:00
1b6f920265 web/flows: improve handling of flow info
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-25 15:01:08 +01:00
3bf8c915d5 web/flows: update flow background image
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-25 15:00:56 +01:00
1cc578be66 website: update comparison
closes #4088

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-25 14:49:09 +01:00
36f8f8bae5 stages/prompt: fix mismatched name field in migration
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-25 14:46:40 +01:00
68058fb2ae stages/authenticator_validate: fix error with passwordless webauthn login, improve tests
closes #4527

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-25 14:45:00 +01:00
0abbe8288e website: update comparison
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

#4088
2023-01-25 11:39:58 +01:00
a9de9101ca web: bump @sentry/browser from 7.32.1 to 7.33.0 in /web (#4516)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.32.1 to 7.33.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.32.1...7.33.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:34:41 +01:00
19ed9dc5e9 web: bump @lingui/macro from 3.16.0 to 3.16.1 in /web (#4518)
Bumps [@lingui/macro](https://github.com/lingui/js-lingui) from 3.16.0 to 3.16.1.
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v3.16.0...v3.16.1)

---
updated-dependencies:
- dependency-name: "@lingui/macro"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:34:28 +01:00
b5ae712f35 website: bump ua-parser-js from 0.7.32 to 0.7.33 in /website (#4526)
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.32 to 0.7.33.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.32...0.7.33)

---
updated-dependencies:
- dependency-name: ua-parser-js
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:25:38 +01:00
b1c01b53e7 web: bump @lingui/cli from 3.16.0 to 3.16.1 in /web (#4522)
Bumps [@lingui/cli](https://github.com/lingui/js-lingui) from 3.16.0 to 3.16.1.
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v3.16.0...v3.16.1)

---
updated-dependencies:
- dependency-name: "@lingui/cli"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:24:11 +01:00
5b31e47573 core: bump goauthentik.io/api/v3 from 3.2023012.1 to 3.2023012.2 (#4523)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023012.1 to 3.2023012.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023012.1...v3.2023012.2)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:23:50 +01:00
d2a9a294f2 web: bump pyright from 1.1.290 to 1.1.291 in /web (#4519)
Bumps [pyright](https://github.com/Microsoft/pyright/tree/HEAD/packages/pyright) from 1.1.290 to 1.1.291.
- [Release notes](https://github.com/Microsoft/pyright/releases)
- [Commits](https://github.com/Microsoft/pyright/commits/1.1.291/packages/pyright)

---
updated-dependencies:
- dependency-name: pyright
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:23:38 +01:00
32c7c58518 web: bump @sentry/tracing from 7.32.1 to 7.33.0 in /web (#4517)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.32.1 to 7.33.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.32.1...7.33.0)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:23:28 +01:00
349c6e50c2 web: bump @lingui/detect-locale from 3.16.0 to 3.16.1 in /web (#4521)
Bumps [@lingui/detect-locale](https://github.com/lingui/js-lingui) from 3.16.0 to 3.16.1.
- [Release notes](https://github.com/lingui/js-lingui/releases)
- [Changelog](https://github.com/lingui/js-lingui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lingui/js-lingui/compare/v3.16.0...v3.16.1)

---
updated-dependencies:
- dependency-name: "@lingui/detect-locale"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:22:50 +01:00
2e8027fa04 core: bump coverage from 7.0.5 to 7.1.0 (#4524)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.0.5 to 7.1.0.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.0.5...7.1.0)

---
updated-dependencies:
- dependency-name: coverage
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 11:22:37 +01:00
0bc1b33663 website/blog: fix mixed up images
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-24 19:46:15 +01:00
8564f9ef87 website: hardcode url for disqus comments
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-24 19:26:04 +01:00
7cfd84d8f0 website/blog: update blog title
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-24 19:03:05 +01:00
f2e40ec7e3 website: publish new blog post
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-24 18:59:54 +01:00
1f1255a829 web: bump API Client version (#4508)
Signed-off-by: GitHub <noreply@github.com>

Signed-off-by: GitHub <noreply@github.com>
2023-01-24 13:45:05 +01:00
53b65a9d1a stages/prompt: field name (#4497)
* add prompt field name

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

* remove numerical prefix

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

* fix missing name

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

* use text field

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

* add description label

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

* add migrate blueprint to remove old stages

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

* add task to remove unretrievable blueprints

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

* lint

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

* fix blueprint test paths

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

* fix tests

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

* actually fix tests

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

* fix tests even more

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

* fix fixtures

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

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-24 12:23:22 +01:00
9437e2d3ab website: add disqus to blog posts
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-24 12:14:59 +01:00
6a7b78abc2 web: bump @sentry/browser from 7.31.1 to 7.32.1 in /web (#4498)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.31.1 to 7.32.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.31.1...7.32.1)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 11:05:12 +01:00
9c24e5195b core: bump goauthentik.io/api/v3 from 3.2023010.1 to 3.2023012.1 (#4500)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023010.1 to 3.2023012.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023010.1...v3.2023012.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:56:29 +01:00
306b046b9a web: bump @sentry/tracing from 7.31.1 to 7.32.1 in /web (#4499)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.31.1 to 7.32.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.31.1...7.32.1)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:56:18 +01:00
10b50c5845 web: bump @typescript-eslint/parser from 5.48.2 to 5.49.0 in /web (#4501)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.48.2 to 5.49.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.49.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:52:08 +01:00
3912a57df2 core: bump sentry-sdk from 1.13.0 to 1.14.0 (#4502)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.13.0...1.14.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:51:38 +01:00
08142d393f web: bump @typescript-eslint/eslint-plugin from 5.48.2 to 5.49.0 in /web (#4503)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.48.2 to 5.49.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.49.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:48:19 +01:00
0f7af256d6 web: bump @types/codemirror from 5.60.6 to 5.60.7 in /web (#4504)
Bumps [@types/codemirror](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/codemirror) from 5.60.6 to 5.60.7.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/codemirror)

---
updated-dependencies:
- dependency-name: "@types/codemirror"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:48:00 +01:00
16076cc46f outposts: fallback to ghcr
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-24 10:47:30 +01:00
8aa16e66e4 core: bump selenium from 4.7.2 to 4.8.0 (#4505)
Bumps [selenium](https://github.com/SeleniumHQ/Selenium) from 4.7.2 to 4.8.0.
- [Release notes](https://github.com/SeleniumHQ/Selenium/releases)
- [Commits](https://github.com/SeleniumHQ/Selenium/commits/selenium-4.8.0)

---
updated-dependencies:
- dependency-name: selenium
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:47:10 +01:00
a9b32e2f97 providers/ldap: add unbind flow execution (#4484)
add unbind flow execution

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

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-23 20:36:30 +01:00
b2d272bf6f api: fix lint
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-23 20:19:03 +01:00
31ef6fb6a6 core: delete session when user is set to inactive
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-23 16:24:30 +01:00
c9c059a008 api: ensure user is active when authenticating
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-23 16:24:30 +01:00
a5e84b5482 web: bump API Client version (#4492)
Signed-off-by: GitHub <noreply@github.com>

Signed-off-by: GitHub <noreply@github.com>
2023-01-23 15:18:33 +00:00
7c697e09f3 Merge branch 'version-2023.1' 2023-01-23 14:39:59 +01:00
b99afd82b2 stages/user_write: fix migration setting wrong value, fix form
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-23 14:38:26 +01:00
88 changed files with 1578 additions and 728 deletions

View File

@ -31,6 +31,16 @@ def validate_auth(header: bytes) -> Optional[str]:
def bearer_auth(raw_header: bytes) -> Optional[User]:
"""raw_header in the Format of `Bearer ....`"""
user = auth_user_lookup(raw_header)
if not user:
return None
if not user.is_active:
raise AuthenticationFailed("Token invalid/expired")
return user
def auth_user_lookup(raw_header: bytes) -> Optional[User]:
"""raw_header in the Format of `Bearer ....`"""
from authentik.providers.oauth2.models import RefreshToken

View File

@ -3,13 +3,12 @@ from base64 import b64encode
from django.conf import settings
from django.test import TestCase
from guardian.shortcuts import get_anonymous_user
from rest_framework.exceptions import AuthenticationFailed
from authentik.api.authentication import bearer_auth
from authentik.blueprints.tests import reconcile_app
from authentik.core.models import USER_ATTRIBUTE_SA, Token, TokenIntents
from authentik.core.tests.utils import create_test_flow
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.lib.generators import generate_id
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
from authentik.providers.oauth2.models import OAuth2Provider, RefreshToken
@ -36,9 +35,18 @@ class TestAPIAuth(TestCase):
def test_bearer_valid(self):
"""Test valid token"""
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=get_anonymous_user())
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=create_test_admin_user())
self.assertEqual(bearer_auth(f"Bearer {token.key}".encode()), token.user)
def test_bearer_valid_deactivated(self):
"""Test valid token"""
user = create_test_admin_user()
user.is_active = False
user.save()
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=user)
with self.assertRaises(AuthenticationFailed):
bearer_auth(f"Bearer {token.key}".encode())
def test_managed_outpost(self):
"""Test managed outpost"""
with self.assertRaises(AuthenticationFailed):
@ -56,7 +64,7 @@ class TestAPIAuth(TestCase):
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
)
refresh = RefreshToken.objects.create(
user=get_anonymous_user(),
user=create_test_admin_user(),
provider=provider,
refresh_token=generate_id(),
_scope=SCOPE_AUTHENTIK_API,
@ -69,7 +77,7 @@ class TestAPIAuth(TestCase):
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
)
refresh = RefreshToken.objects.create(
user=get_anonymous_user(),
user=create_test_admin_user(),
provider=provider,
refresh_token=generate_id(),
_scope="",

View File

@ -57,9 +57,10 @@ class AuthentikBlueprintsConfig(ManagedAppConfig):
def reconcile_blueprints_discover(self):
"""Run blueprint discovery"""
from authentik.blueprints.v1.tasks import blueprints_discover
from authentik.blueprints.v1.tasks import blueprints_discover, clear_failed_blueprints
blueprints_discover.delay()
clear_failed_blueprints.delay()
def import_models(self):
super().import_models()

View File

@ -9,4 +9,9 @@ CELERY_BEAT_SCHEDULE = {
"schedule": crontab(minute=fqdn_rand("blueprints_v1_discover"), hour="*"),
"options": {"queue": "authentik_scheduled"},
},
"blueprints_v1_cleanup": {
"task": "authentik.blueprints.v1.tasks.clear_failed_blueprints",
"schedule": crontab(minute=fqdn_rand("blueprints_v1_cleanup"), hour="*"),
"options": {"queue": "authentik_scheduled"},
},
}

View File

@ -4,6 +4,7 @@ entries:
pk: cb954fd4-65a5-4ad9-b1ee-180ee9559cf4
model: authentik_stages_prompt.prompt
attrs:
name: qwerweqrq
field_key: username
label: Username
type: username

View File

@ -13,7 +13,7 @@ from authentik.tenants.models import Tenant
class TestPackaged(TransactionTestCase):
"""Empty class, test methods are added dynamically"""
@apply_blueprint("default/90-default-tenant.yaml")
@apply_blueprint("default/default-tenant.yaml")
def test_decorator_static(self):
"""Test @apply_blueprint decorator"""
self.assertTrue(Tenant.objects.filter(domain="authentik-default").exists())

View File

@ -262,15 +262,21 @@ class TestBlueprintsV1(TransactionTestCase):
with transaction_rollback():
# First stage fields
username_prompt = Prompt.objects.create(
field_key="username", label="Username", order=0, type=FieldTypes.TEXT
name=generate_id(),
field_key="username",
label="Username",
order=0,
type=FieldTypes.TEXT,
)
password = Prompt.objects.create(
name=generate_id(),
field_key="password",
label="Password",
order=1,
type=FieldTypes.PASSWORD,
)
password_repeat = Prompt.objects.create(
name=generate_id(),
field_key="password_repeat",
label="Password (repeat)",
order=2,

View File

@ -3,3 +3,4 @@
LABEL_AUTHENTIK_SYSTEM = "blueprints.goauthentik.io/system"
LABEL_AUTHENTIK_INSTANTIATE = "blueprints.goauthentik.io/instantiate"
LABEL_AUTHENTIK_GENERATED = "blueprints.goauthentik.io/generated"
LABEL_AUTHENTIK_DESCRIPTION = "blueprints.goauthentik.io/description"

View File

@ -219,3 +219,14 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
finally:
if instance:
instance.save()
@CELERY_APP.task()
def clear_failed_blueprints():
"""Remove blueprints which couldn't be fetched"""
# Exclude OCI blueprints as those might be temporarily unavailable
for blueprint in BlueprintInstance.objects.exclude(path__startswith="oci://"):
try:
blueprint.retrieve()
except BlueprintRetrievalFailed:
blueprint.delete()

View File

@ -4,6 +4,8 @@ from json import loads
from typing import Any, Optional
from django.contrib.auth import update_session_auth_hash
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
from django.db.models.functions import ExtractHour
from django.db.models.query import QuerySet
from django.db.transaction import atomic
@ -57,6 +59,7 @@ from authentik.core.models import (
USER_ATTRIBUTE_SA,
USER_ATTRIBUTE_TOKEN_EXPIRING,
USER_PATH_SERVICE_ACCOUNT,
AuthenticatedSession,
Group,
Token,
TokenIntents,
@ -561,3 +564,14 @@ class UserViewSet(UsedByMixin, ModelViewSet):
)
}
)
def partial_update(self, request: Request, *args, **kwargs) -> Response:
response = super().partial_update(request, *args, **kwargs)
instance: User = self.get_object()
if not instance.is_active:
sessions = AuthenticatedSession.objects.filter(user=instance)
session_ids = sessions.values_list("session_key", flat=True)
cache.delete_many(f"{KEY_PREFIX}{session}" for session in session_ids)
sessions.delete()
LOGGER.debug("Deleted user's sessions", user=instance.username)
return response

View File

@ -1,10 +1,12 @@
"""Test Users API"""
from json import loads
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
from django.urls.base import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.core.models import AuthenticatedSession, User
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_tenant
from authentik.flows.models import FlowDesignation
from authentik.lib.config import CONFIG
@ -257,3 +259,26 @@ class TestUsersAPI(APITestCase):
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())
self.assertEqual(body["user"]["avatar"], "bar")
def test_session_delete(self):
"""Ensure sessions are deleted when a user is deactivated"""
user = create_test_admin_user()
session_id = generate_id()
AuthenticatedSession.objects.create(
user=user,
session_key=session_id,
last_ip="",
)
cache.set(KEY_PREFIX + session_id, "foo")
self.client.force_login(self.admin)
response = self.client.patch(
reverse("authentik_api:user-detail", kwargs={"pk": user.pk}),
data={
"is_active": False,
},
)
self.assertEqual(response.status_code, 200)
self.assertIsNone(cache.get(KEY_PREFIX + session_id))
self.assertFalse(AuthenticatedSession.objects.filter(session_key=session_id).exists())

View File

@ -5,13 +5,20 @@ from contextlib import contextmanager
from glob import glob
from json import dumps, loads
from json.decoder import JSONDecodeError
from pathlib import Path
from sys import argv, stderr
from time import time
from typing import Any
from typing import Any, Optional
from urllib.parse import urlparse
import yaml
from django.conf import ImproperlyConfigured
from watchdog.events import (
FileModifiedEvent,
FileSystemEvent,
FileSystemEventHandler,
)
from watchdog.observers import Observer
SEARCH_PATHS = ["authentik/lib/default.yml", "/etc/authentik/config.yml", ""] + glob(
"/etc/authentik/config.d/*.yml", recursive=True
@ -38,9 +45,47 @@ class ConfigLoader:
A variable like AUTHENTIK_POSTGRESQL__HOST would translate to postgresql.host"""
loaded_file = []
observer: Observer
class FSObserver(FileSystemEventHandler):
"""File system observer"""
loader: "ConfigLoader"
path: str
container: Optional[dict] = None
key: Optional[str] = None
def __init__(
self,
loader: "ConfigLoader",
path: str,
container: Optional[dict] = None,
key: Optional[str] = None,
) -> None:
super().__init__()
self.loader = loader
self.path = path
self.container = container
self.key = key
def on_any_event(self, event: FileSystemEvent):
if not isinstance(event, FileModifiedEvent):
return
if event.is_directory:
return
if event.src_path != self.path:
return
if self.container and self.key:
with open(self.path, "r", encoding="utf8") as _file:
self.container[self.key] = _file.read()
else:
self.loader.log("info", "Updating from changed file", file=self.path)
self.loader.update_from_file(self.path, watch=False)
def __init__(self):
super().__init__()
self.observer = Observer()
self.observer.start()
self.__config = {}
base_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), "../.."))
for path in SEARCH_PATHS:
@ -81,11 +126,11 @@ class ConfigLoader:
root[key] = self.update(root.get(key, {}), value)
else:
if isinstance(value, str):
value = self.parse_uri(value)
value = self.parse_uri(value, root, key)
root[key] = value
return root
def parse_uri(self, value: str) -> str:
def parse_uri(self, value: str, container: dict[str, Any], key: Optional[str] = None, ) -> str:
"""Parse string values which start with a URI"""
url = urlparse(value)
if url.scheme == "env":
@ -93,13 +138,23 @@ class ConfigLoader:
if url.scheme == "file":
try:
with open(url.path, "r", encoding="utf8") as _file:
value = _file.read().strip()
value = _file.read()
if key:
self.observer.schedule(
ConfigLoader.FSObserver(
self,
url.path,
container,
key,
),
Path(url.path).parent,
)
except OSError as exc:
self.log("error", f"Failed to read config value from {url.path}: {exc}")
value = url.query
return value
def update_from_file(self, path: str):
def update_from_file(self, path: str, watch=True):
"""Update config from file contents"""
try:
with open(path, encoding="utf8") as file:
@ -107,6 +162,8 @@ class ConfigLoader:
self.update(self.__config, yaml.safe_load(file))
self.log("debug", "Loaded config", file=path)
self.loaded_file.append(path)
if watch:
self.observer.schedule(ConfigLoader.FSObserver(self, path), Path(path).parent)
except yaml.YAMLError as exc:
raise ImproperlyConfigured from exc
except PermissionError as exc:
@ -181,13 +238,12 @@ class ConfigLoader:
if comp not in root:
root[comp] = {}
root = root.get(comp, {})
root[path_parts[-1]] = value
self.parse_uri(value, root, path_parts[-1])
def y_bool(self, path: str, default=False) -> bool:
"""Wrapper for y that converts value into boolean"""
return str(self.y(path, default)).lower() == "true"
CONFIG = ConfigLoader()
if __name__ == "__main__":

View File

@ -5,7 +5,7 @@ from tempfile import mkstemp
from django.conf import ImproperlyConfigured
from django.test import TestCase
from authentik.lib.config import ENV_PREFIX, ConfigLoader
from authentik.lib.config import CONFIG, ENV_PREFIX, ConfigLoader
class TestConfig(TestCase):
@ -31,8 +31,8 @@ class TestConfig(TestCase):
"""Test URI parsing (environment)"""
config = ConfigLoader()
environ["foo"] = "bar"
self.assertEqual(config.parse_uri("env://foo"), "bar")
self.assertEqual(config.parse_uri("env://foo?bar"), "bar")
self.assertEqual(config.parse_uri("env://foo", {}), "bar")
self.assertEqual(config.parse_uri("env://foo?bar", {}), "bar")
def test_uri_file(self):
"""Test URI parsing (file load)"""
@ -41,8 +41,8 @@ class TestConfig(TestCase):
write(file, "foo".encode())
_, file2_name = mkstemp()
chmod(file2_name, 0o000) # Remove all permissions so we can't read the file
self.assertEqual(config.parse_uri(f"file://{file_name}"), "foo")
self.assertEqual(config.parse_uri(f"file://{file2_name}?def"), "def")
self.assertEqual(config.parse_uri(f"file://{file_name}", {}), "foo")
self.assertEqual(config.parse_uri(f"file://{file2_name}?def", {}), "def")
unlink(file_name)
unlink(file2_name)
@ -59,3 +59,13 @@ class TestConfig(TestCase):
config.update_from_file(file2_name)
unlink(file_name)
unlink(file2_name)
def test_update(self):
"""Test change to file"""
file, file_name = mkstemp()
write(file, b"test")
CONFIG.y_set("test.file", f"file://{file_name}")
self.assertEqual(CONFIG.y("test.file"), "test")
write(file, "test2")
self.assertEqual(CONFIG.y("test.file"), "test2")
unlink(file_name)

View File

@ -183,7 +183,7 @@ class DockerController(BaseController):
try:
self.client.images.pull(image)
except DockerException: # pragma: no cover
image = f"goauthentik.io/{self.outpost.type}:latest"
image = f"ghcr.io/goauthentik/{self.outpost.type}:latest"
self.client.images.pull(image)
return image

View File

@ -4,6 +4,7 @@ from django.urls.base import reverse
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.models import FlowDesignation, FlowStageBinding
from authentik.flows.tests import FlowTestCase
from authentik.lib.generators import generate_id
from authentik.policies.password.models import PasswordPolicy
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
@ -16,6 +17,7 @@ class TestPasswordPolicyFlow(FlowTestCase):
self.flow = create_test_flow(FlowDesignation.AUTHENTICATION)
password_prompt = Prompt.objects.create(
name=generate_id(),
field_key="password",
label="PASSWORD_LABEL",
type=FieldTypes.PASSWORD,

View File

@ -376,7 +376,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
def challenge_valid(self, response: AuthenticatorValidationChallengeResponse) -> HttpResponse:
# All validation is done by the serializer
user = self.get_pending_user()
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
if not user:
if "webauthn" not in response.data:
return self.executor.stage_invalid()

View File

@ -9,9 +9,10 @@ from webauthn.helpers.bytes_to_base64url import bytes_to_base64url
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.models import FlowStageBinding, NotConfiguredAction
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.stage import StageView
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import FlowExecutorView
from authentik.flows.views.executor import SESSION_KEY_PLAN, FlowExecutorView
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.stages.authenticator_validate.challenge import (
@ -20,10 +21,14 @@ from authentik.stages.authenticator_validate.challenge import (
validate_challenge_webauthn,
)
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
from authentik.stages.authenticator_validate.stage import AuthenticatorValidateStageView
from authentik.stages.authenticator_validate.stage import (
SESSION_KEY_DEVICE_CHALLENGES,
AuthenticatorValidateStageView,
)
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
from authentik.stages.authenticator_webauthn.stage import SESSION_KEY_WEBAUTHN_CHALLENGE
from authentik.stages.identification.models import IdentificationStage, UserFields
from authentik.stages.user_login.models import UserLoginStage
class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
@ -185,10 +190,7 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
def test_validate_challenge(self):
"""Test webauthn"""
request = get_request("/")
request.user = self.user
WebAuthnDevice.objects.create(
device = WebAuthnDevice.objects.create(
user=self.user,
public_key=(
"pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J"
@ -204,49 +206,134 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
not_configured_action=NotConfiguredAction.CONFIGURE,
device_classes=[DeviceClasses.WEBAUTHN],
)
stage_view = AuthenticatorValidateStageView(
FlowExecutorView(flow=flow, current_stage=stage), request=request
)
request = get_request("/")
request.session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes(
session = self.client.session
plan = FlowPlan(flow_pk=flow.pk.hex)
plan.append_stage(stage)
plan.append_stage(UserLoginStage(name=generate_id()))
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
session[SESSION_KEY_PLAN] = plan
session[SESSION_KEY_DEVICE_CHALLENGES] = [
{
"device_class": device.__class__.__name__.lower().replace("device", ""),
"device_uid": device.pk,
"challenge": {},
}
]
session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes(
(
"g98I51mQvZXo5lxLfhrD2zfolhZbLRyCgqkkYap1"
"jwSaJ13BguoJWCF9_Lg3AgO4Wh-Bqa556JE20oKsYbl6RA"
)
)
request.session.save()
session.save()
stage_view = AuthenticatorValidateStageView(
FlowExecutorView(flow=flow, current_stage=stage), request=request
)
request.META["SERVER_NAME"] = "localhost"
request.META["SERVER_PORT"] = "9000"
validate_challenge_webauthn(
{
"id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
"rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
"type": "public-key",
"assertionClientExtensions": "{}",
"response": {
"clientDataJSON": (
"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4TGZo"
"ckQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdoLUJxYTU1"
"NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjkwMDAiLCJjcm9z"
"c09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUiOiJkbyBub3Qg"
"Y29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczov"
"L2dvby5nbC95YWJQZXgifQ==",
),
"signature": (
"MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI"
"AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag=="
),
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==",
"userHandle": None,
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
data={
"webauthn": {
"id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
"rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
"type": "public-key",
"assertionClientExtensions": "{}",
"response": {
"clientDataJSON": (
"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4T"
"GZockQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdoLU"
"JxYTU1NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjkwMDA"
"iLCJjcm9zc09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUi"
"OiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuI"
"FNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ==",
),
"signature": (
"MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI"
"AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag=="
),
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==",
"userHandle": None,
},
},
},
stage_view,
self.user,
SERVER_NAME="localhost",
SERVER_PORT="9000",
)
self.assertEqual(response.status_code, 302)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
def test_validate_challenge_userless(self):
"""Test webauthn"""
device = WebAuthnDevice.objects.create(
user=self.user,
public_key=(
"pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J"
"H6IlggLTXytuhzFVYYAK4PQNj8_coGrbbzSfUxdiPAcZTQCyU"
),
credential_id="QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
sign_count=4,
rp_id=generate_id(),
)
flow = create_test_flow()
stage = AuthenticatorValidateStage.objects.create(
name=generate_id(),
not_configured_action=NotConfiguredAction.CONFIGURE,
device_classes=[DeviceClasses.WEBAUTHN],
)
session = self.client.session
plan = FlowPlan(flow_pk=flow.pk.hex)
plan.append_stage(stage)
plan.append_stage(UserLoginStage(name=generate_id()))
session[SESSION_KEY_PLAN] = plan
session[SESSION_KEY_DEVICE_CHALLENGES] = [
{
"device_class": device.__class__.__name__.lower().replace("device", ""),
"device_uid": device.pk,
"challenge": {},
}
]
session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes(
(
"g98I51mQvZXo5lxLfhrD2zfolhZbLRyCgqkkYap1"
"jwSaJ13BguoJWCF9_Lg3AgO4Wh-Bqa556JE20oKsYbl6RA"
)
)
session.save()
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
data={
"webauthn": {
"id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
"rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
"type": "public-key",
"assertionClientExtensions": "{}",
"response": {
"clientDataJSON": (
"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4T"
"GZockQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdoLU"
"JxYTU1NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjkwMDA"
"iLCJjcm9zc09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUi"
"OiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuI"
"FNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ==",
),
"signature": (
"MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI"
"AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag=="
),
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==",
"userHandle": None,
},
},
},
SERVER_NAME="localhost",
SERVER_PORT="9000",
)
self.assertEqual(response.status_code, 302)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
def test_validate_challenge_invalid(self):
"""Test webauthn"""

View File

@ -42,6 +42,7 @@ class PromptSerializer(ModelSerializer):
model = Prompt
fields = [
"pk",
"name",
"field_key",
"label",
"type",
@ -59,5 +60,5 @@ class PromptViewSet(UsedByMixin, ModelViewSet):
queryset = Prompt.objects.all().prefetch_related("promptstage_set")
serializer_class = PromptSerializer
filterset_fields = ["field_key", "label", "type", "placeholder"]
search_fields = ["field_key", "label", "type", "placeholder"]
filterset_fields = ["field_key", "name", "label", "type", "placeholder"]
search_fields = ["field_key", "name", "label", "type", "placeholder"]

View File

@ -0,0 +1,40 @@
# Generated by Django 4.1.5 on 2023-01-23 19:42
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def set_generated_name(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Prompt = apps.get_model("authentik_stages_prompt", "prompt")
for prompt in Prompt.objects.using(db_alias).all():
name = prompt.field_key
stage = prompt.promptstage_set.order_by("name").first()
if stage:
name += "_" + stage.name
prompt.name = name
prompt.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_prompt", "0008_alter_prompt_type"),
]
operations = [
migrations.AddField(
model_name="prompt",
name="name",
field=models.TextField(default="", unique=False, db_index=False, blank=False),
preserve_default=False,
),
migrations.RunPython(code=set_generated_name),
migrations.AlterField(
model_name="prompt",
name="name",
field=models.TextField(unique=True),
),
]

View File

@ -96,6 +96,7 @@ class Prompt(SerializerModel):
"""Single Prompt, part of a prompt stage."""
prompt_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.TextField(unique=True, blank=False)
field_key = models.TextField(
help_text=_("Name of the form field, also used to store the value")

View File

@ -30,6 +30,7 @@ class TestPromptStage(FlowTestCase):
self.factory = RequestFactory()
self.flow = create_test_flow()
username_prompt = Prompt.objects.create(
name=generate_id(),
field_key="username_prompt",
label="USERNAME_LABEL",
type=FieldTypes.USERNAME,
@ -37,6 +38,7 @@ class TestPromptStage(FlowTestCase):
placeholder="USERNAME_PLACEHOLDER",
)
text_prompt = Prompt.objects.create(
name=generate_id(),
field_key="text_prompt",
label="TEXT_LABEL",
type=FieldTypes.TEXT,
@ -44,6 +46,7 @@ class TestPromptStage(FlowTestCase):
placeholder="TEXT_PLACEHOLDER",
)
email_prompt = Prompt.objects.create(
name=generate_id(),
field_key="email_prompt",
label="EMAIL_LABEL",
type=FieldTypes.EMAIL,
@ -51,6 +54,7 @@ class TestPromptStage(FlowTestCase):
placeholder="EMAIL_PLACEHOLDER",
)
password_prompt = Prompt.objects.create(
name=generate_id(),
field_key="password_prompt",
label="PASSWORD_LABEL",
type=FieldTypes.PASSWORD,
@ -58,6 +62,7 @@ class TestPromptStage(FlowTestCase):
placeholder="PASSWORD_PLACEHOLDER",
)
password2_prompt = Prompt.objects.create(
name=generate_id(),
field_key="password2_prompt",
label="PASSWORD_LABEL",
type=FieldTypes.PASSWORD,
@ -65,6 +70,7 @@ class TestPromptStage(FlowTestCase):
placeholder="PASSWORD_PLACEHOLDER",
)
number_prompt = Prompt.objects.create(
name=generate_id(),
field_key="number_prompt",
label="NUMBER_LABEL",
type=FieldTypes.NUMBER,
@ -72,12 +78,14 @@ class TestPromptStage(FlowTestCase):
placeholder="NUMBER_PLACEHOLDER",
)
hidden_prompt = Prompt.objects.create(
name=generate_id(),
field_key="hidden_prompt",
type=FieldTypes.HIDDEN,
required=True,
placeholder="HIDDEN_PLACEHOLDER",
)
static_prompt = Prompt.objects.create(
name=generate_id(),
field_key="static_prompt",
type=FieldTypes.STATIC,
required=True,

View File

@ -17,9 +17,10 @@ entries:
placeholder_expression: false
required: true
type: text
identifiers:
field_key: username
label: Username
identifiers:
name: default-source-enrollment-field-username
id: prompt-field-username
model: authentik_stages_prompt.prompt
- attrs:

View File

@ -21,9 +21,10 @@ entries:
placeholder_expression: true
required: true
type: text
identifiers:
field_key: username
label: Username
identifiers:
name: default-user-settings-field-username
id: prompt-field-username
model: authentik_stages_prompt.prompt
- attrs:
@ -36,9 +37,10 @@ entries:
placeholder_expression: true
required: true
type: text
identifiers:
field_key: name
label: Name
identifiers:
name: default-user-settings-field-name
id: prompt-field-name
model: authentik_stages_prompt.prompt
- attrs:
@ -51,9 +53,10 @@ entries:
placeholder_expression: true
required: true
type: email
identifiers:
field_key: email
label: Email
identifiers:
name: default-user-settings-field-email
id: prompt-field-email
model: authentik_stages_prompt.prompt
- attrs:
@ -66,9 +69,10 @@ entries:
placeholder_expression: true
required: true
type: ak-locale
identifiers:
field_key: attributes.settings.locale
label: Locale
identifiers:
name: default-user-settings-field-locale
id: prompt-field-locale
model: authentik_stages_prompt.prompt
- attrs:

View File

@ -19,10 +19,11 @@ entries:
required: true
sub_text: ''
type: static
id: prompt-field-header
identifiers:
field_key: oobe-header-text
label: oobe-header-text
id: prompt-field-header
identifiers:
name: initial-setup-field-header
model: authentik_stages_prompt.prompt
- attrs:
order: 101
@ -31,10 +32,11 @@ entries:
required: true
sub_text: ''
type: email
field_key: email
label: Email
id: prompt-field-email
identifiers:
field_key: admin_email
label: Email
name: initial-setup-field-email
model: authentik_stages_prompt.prompt
- attrs:
order: 300
@ -43,10 +45,11 @@ entries:
required: true
sub_text: ''
type: password
id: prompt-field-password
identifiers:
field_key: password
label: Password
id: prompt-field-password
identifiers:
name: initial-setup-field-password
model: authentik_stages_prompt.prompt
- attrs:
order: 301
@ -55,10 +58,11 @@ entries:
required: true
sub_text: ''
type: password
id: prompt-field-password-repeat
identifiers:
field_key: password_repeat
label: Password (repeat)
id: prompt-field-password-repeat
identifiers:
name: initial-setup-field-password-repeat
model: authentik_stages_prompt.prompt
- attrs:
expression: |
@ -66,8 +70,6 @@ entries:
# by injecting "pending_user"
akadmin = ak_user_by(username="akadmin")
context["flow_plan"].context["pending_user"] = akadmin
# Remap the email value
context["prompt_data"]["email"] = context["prompt_data"]["admin_email"]
return True
id: policy-default-oobe-prefill-user
identifiers:

View File

@ -17,9 +17,10 @@ entries:
placeholder_expression: false
required: true
type: password
identifiers:
field_key: password
label: Password
identifiers:
name: default-password-change-field-password
id: prompt-field-password
model: authentik_stages_prompt.prompt
- attrs:
@ -28,9 +29,10 @@ entries:
placeholder_expression: false
required: true
type: password
identifiers:
field_key: password_repeat
label: Password (repeat)
identifiers:
name: default-password-change-field-password-repeat
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
- attrs:

View File

@ -13,56 +13,61 @@ entries:
title: Welcome to authentik!
designation: enrollment
authentication: require_unauthenticated
- identifiers:
- id: prompt-field-username
model: authentik_stages_prompt.prompt
identifiers:
name: default-enrollment-field-username
attrs:
field_key: username
label: Username
id: prompt-field-username
model: authentik_stages_prompt.prompt
attrs:
type: username
required: true
placeholder: Username
placeholder_expression: false
order: 0
- identifiers:
field_key: password
label: Password
name: default-enrollment-field-password
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
field_key: password
label: Password
type: password
required: true
placeholder: Password
placeholder_expression: false
order: 0
- identifiers:
field_key: password_repeat
label: Password (repeat)
name: default-enrollment-field-password-repeat
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat
label: Password (repeat)
type: password
required: true
placeholder: Password (repeat)
placeholder_expression: false
order: 1
- identifiers:
field_key: name
label: Name
name: default-enrollment-field-name
id: prompt-field-name
model: authentik_stages_prompt.prompt
attrs:
field_key: name
label: Name
type: text
required: true
placeholder: Name
placeholder_expression: false
order: 0
- identifiers:
field_key: email
label: Email
name: default-enrollment-field-email
id: prompt-field-email
model: authentik_stages_prompt.prompt
attrs:
field_key: email
label: Email
type: email
required: true
placeholder: Email

View File

@ -14,55 +14,60 @@ entries:
designation: enrollment
authentication: require_unauthenticated
- identifiers:
field_key: username
label: Username
name: default-enrollment-field-username
id: prompt-field-username
model: authentik_stages_prompt.prompt
attrs:
field_key: username
label: Username
type: username
required: true
placeholder: Username
placeholder_expression: false
order: 0
- identifiers:
field_key: password
label: Password
name: default-enrollment-field-password
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
field_key: password
label: Password
type: password
required: true
placeholder: Password
placeholder_expression: false
order: 0
- identifiers:
field_key: password_repeat
label: Password (repeat)
name: default-enrollment-field-password-repeat
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat
label: Password (repeat)
type: password
required: true
placeholder: Password (repeat)
placeholder_expression: false
order: 1
- identifiers:
field_key: name
label: Name
name: default-enrollment-field-name
id: prompt-field-name
model: authentik_stages_prompt.prompt
attrs:
field_key: name
label: Name
type: text
required: true
placeholder: Name
placeholder_expression: false
order: 0
- identifiers:
field_key: email
label: Email
name: default-enrollment-field-email
id: prompt-field-email
model: authentik_stages_prompt.prompt
attrs:
field_key: email
label: Email
type: email
required: true
placeholder: Email

View File

@ -14,22 +14,24 @@ entries:
designation: recovery
authentication: require_unauthenticated
- identifiers:
field_key: password
label: Password
name: default-recovery-field-password
id: prompt-field-password
model: authentik_stages_prompt.prompt
attrs:
field_key: password
label: Password
type: password
required: true
placeholder: Password
order: 0
placeholder_expression: false
- identifiers:
field_key: password_repeat
label: Password (repeat)
name: default-recovery-field-password-repeat
id: prompt-field-password-repeat
model: authentik_stages_prompt.prompt
attrs:
field_key: password_repeat
label: Password (repeat)
type: password
required: true
placeholder: Password (repeat)

View File

@ -0,0 +1,70 @@
version: 1
metadata:
labels:
blueprints.goauthentik.io/description: Migrate to 2023.2, remove unused prompt fields
name: Migration - Remove old prompt fields
entries:
- model: authentik_stages_prompt.prompt
identifiers:
name: admin_email_stage-default-oobe-password
attrs:
field_key: foo
label: foo
type: text
state: absent
- model: authentik_stages_prompt.prompt
identifiers:
name: attributes.settings.locale_default-user-settings
attrs:
field_key: foo
label: foo
type: text
state: absent
- model: authentik_stages_prompt.prompt
identifiers:
name: email_default-user-settings
attrs:
field_key: foo
label: foo
type: text
state: absent
- model: authentik_stages_prompt.prompt
identifiers:
name: name_default-user-settings
attrs:
field_key: foo
label: foo
type: text
state: absent
- model: authentik_stages_prompt.prompt
identifiers:
name: oobe-header-text_stage-default-oobe-password
attrs:
field_key: foo
label: foo
type: text
state: absent
- model: authentik_stages_prompt.prompt
identifiers:
name: password_default-password-change-prompt
attrs:
field_key: foo
label: foo
type: text
state: absent
- model: authentik_stages_prompt.prompt
identifiers:
name: password_repeat_default-password-change-prompt
attrs:
field_key: foo
label: foo
type: text
state: absent
- model: authentik_stages_prompt.prompt
identifiers:
name: username_default-source-enrollment-prompt
attrs:
field_key: foo
label: foo
type: text
state: absent

2
go.mod
View File

@ -25,7 +25,7 @@ require (
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.1
goauthentik.io/api/v3 v3.2023010.1
goauthentik.io/api/v3 v3.2023012.2
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b

4
go.sum
View File

@ -382,8 +382,8 @@ go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZp
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
goauthentik.io/api/v3 v3.2023010.1 h1:DQuf201pD+OKVnzpLKCaKw8IS397ewzMzFIGXyhmQ3w=
goauthentik.io/api/v3 v3.2023010.1/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
goauthentik.io/api/v3 v3.2023012.2 h1:aP/JlZCxGaPuV+vPabL3Niz7lWAYnNolrINYSFBDFG0=
goauthentik.io/api/v3 v3.2023012.2/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
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-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=

View File

@ -1,8 +1,11 @@
package ak
import "context"
type Outpost interface {
Start() error
Stop() error
Refresh() error
TimerFlowCacheExpiry()
TimerFlowCacheExpiry(context.Context)
Type() string
}

View File

@ -1,15 +1,16 @@
package ak
import (
"context"
"time"
)
func (a *APIController) startPeriodicalTasks() {
go a.Server.TimerFlowCacheExpiry()
go func() {
for range time.Tick(time.Duration(a.GlobalConfig.CacheTimeoutFlows) * time.Second) {
a.logger.WithField("timer", "cache-timeout").Debug("Running periodical tasks")
a.Server.TimerFlowCacheExpiry()
}
}()
ctx, canc := context.WithCancel(context.Background())
defer canc()
go a.Server.TimerFlowCacheExpiry(ctx)
for range time.Tick(time.Duration(a.GlobalConfig.CacheTimeoutFlows) * time.Second) {
a.logger.WithField("timer", "cache-timeout").Debug("Running periodical tasks")
a.Server.TimerFlowCacheExpiry(ctx)
}
}

View File

@ -143,6 +143,10 @@ func (fe *FlowExecutor) GetSession() *http.Cookie {
return fe.session
}
func (fe *FlowExecutor) SetSession(s *http.Cookie) {
fe.session = s
}
// WarmUp Ensure authentik's flow cache is warmed up
func (fe *FlowExecutor) WarmUp() error {
gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.get_challenge")

View File

@ -1,9 +1,14 @@
package bind
import "github.com/nmcclain/ldap"
import (
"context"
"github.com/nmcclain/ldap"
)
type Binder interface {
GetUsername(string) (string, error)
Bind(username string, req *Request) (ldap.LDAPResultCode, error)
TimerFlowCacheExpiry()
Unbind(username string, req *Request) (ldap.LDAPResultCode, error)
TimerFlowCacheExpiry(context.Context)
}

View File

@ -0,0 +1,98 @@
package direct
import (
"context"
"github.com/getsentry/sentry-go"
"github.com/nmcclain/ldap"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/flow"
"goauthentik.io/internal/outpost/ldap/bind"
"goauthentik.io/internal/outpost/ldap/flags"
"goauthentik.io/internal/outpost/ldap/metrics"
)
func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResultCode, error) {
fe := flow.NewFlowExecutor(req.Context(), db.si.GetAuthenticationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{
"bindDN": req.BindDN,
"client": req.RemoteAddr(),
"requestId": req.ID(),
})
fe.DelegateClientIP(req.RemoteAddr())
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
fe.Answers[flow.StageIdentification] = username
fe.Answers[flow.StagePassword] = req.BindPW
passed, err := fe.Execute()
flags := flags.UserFlags{
Session: fe.GetSession(),
}
db.si.SetFlags(req.BindDN, &flags)
if err != nil {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "flow_error",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to execute flow")
return ldap.LDAPResultInvalidCredentials, nil
}
if !passed {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "invalid_credentials",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().Info("Invalid credentials")
return ldap.LDAPResultInvalidCredentials, nil
}
access, err := fe.CheckApplicationAccess(db.si.GetAppSlug())
if !access {
req.Log().Info("Access denied for user")
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_denied",
"app": db.si.GetAppSlug(),
}).Inc()
return ldap.LDAPResultInsufficientAccessRights, nil
}
if err != nil {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_check_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to check access")
return ldap.LDAPResultOperationsError, nil
}
req.Log().Info("User has access")
uisp := sentry.StartSpan(req.Context(), "authentik.providers.ldap.bind.user_info")
// Get user info to store in context
userInfo, _, err := fe.ApiClient().CoreApi.CoreUsersMeRetrieve(context.Background()).Execute()
if err != nil {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "user_info_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to get user info")
return ldap.LDAPResultOperationsError, nil
}
cs := db.SearchAccessCheck(userInfo.User)
flags.UserPk = userInfo.User.Pk
flags.CanSearch = cs != nil
db.si.SetFlags(req.BindDN, &flags)
if flags.CanSearch {
req.Log().WithField("group", cs).Info("Allowed access to search")
}
uisp.Finish()
return ldap.LDAPResultSuccess, nil
}

View File

@ -5,16 +5,10 @@ import (
"errors"
"strings"
"github.com/getsentry/sentry-go"
goldap "github.com/go-ldap/ldap/v3"
"github.com/nmcclain/ldap"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"goauthentik.io/api/v3"
"goauthentik.io/internal/outpost/flow"
"goauthentik.io/internal/outpost/ldap/bind"
"goauthentik.io/internal/outpost/ldap/flags"
"goauthentik.io/internal/outpost/ldap/metrics"
"goauthentik.io/internal/outpost/ldap/server"
"goauthentik.io/internal/outpost/ldap/utils"
)
@ -53,90 +47,6 @@ func (db *DirectBinder) GetUsername(dn string) (string, error) {
return "", errors.New("failed to find cn")
}
func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResultCode, error) {
fe := flow.NewFlowExecutor(req.Context(), db.si.GetFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{
"bindDN": req.BindDN,
"client": req.RemoteAddr(),
"requestId": req.ID(),
})
fe.DelegateClientIP(req.RemoteAddr())
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
fe.Answers[flow.StageIdentification] = username
fe.Answers[flow.StagePassword] = req.BindPW
passed, err := fe.Execute()
flags := flags.UserFlags{
Session: fe.GetSession(),
}
db.si.SetFlags(req.BindDN, flags)
if err != nil {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "flow_error",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to execute flow")
return ldap.LDAPResultInvalidCredentials, nil
}
if !passed {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "invalid_credentials",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().Info("Invalid credentials")
return ldap.LDAPResultInvalidCredentials, nil
}
access, err := fe.CheckApplicationAccess(db.si.GetAppSlug())
if !access {
req.Log().Info("Access denied for user")
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_denied",
"app": db.si.GetAppSlug(),
}).Inc()
return ldap.LDAPResultInsufficientAccessRights, nil
}
if err != nil {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_check_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to check access")
return ldap.LDAPResultOperationsError, nil
}
req.Log().Info("User has access")
uisp := sentry.StartSpan(req.Context(), "authentik.providers.ldap.bind.user_info")
// Get user info to store in context
userInfo, _, err := fe.ApiClient().CoreApi.CoreUsersMeRetrieve(context.Background()).Execute()
if err != nil {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "user_info_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to get user info")
return ldap.LDAPResultOperationsError, nil
}
cs := db.SearchAccessCheck(userInfo.User)
flags.UserPk = userInfo.User.Pk
flags.CanSearch = cs != nil
db.si.SetFlags(req.BindDN, flags)
if flags.CanSearch {
req.Log().WithField("group", cs).Info("Allowed access to search")
}
uisp.Finish()
return ldap.LDAPResultSuccess, nil
}
// SearchAccessCheck Check if the current user is allowed to search
func (db *DirectBinder) SearchAccessCheck(user api.UserSelf) *string {
for _, group := range user.Groups {
@ -153,8 +63,8 @@ func (db *DirectBinder) SearchAccessCheck(user api.UserSelf) *string {
return nil
}
func (db *DirectBinder) TimerFlowCacheExpiry() {
fe := flow.NewFlowExecutor(context.Background(), db.si.GetFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{})
func (db *DirectBinder) TimerFlowCacheExpiry(ctx context.Context) {
fe := flow.NewFlowExecutor(ctx, db.si.GetAuthenticationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{})
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
fe.Params.Add("goauthentik.io/outpost/ldap-warmup", "true")

View File

@ -0,0 +1,29 @@
package direct
import (
"github.com/nmcclain/ldap"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/flow"
"goauthentik.io/internal/outpost/ldap/bind"
)
func (db *DirectBinder) Unbind(username string, req *bind.Request) (ldap.LDAPResultCode, error) {
flags := db.si.GetFlags(req.BindDN)
if flags == nil || flags.Session == nil {
return ldap.LDAPResultSuccess, nil
}
fe := flow.NewFlowExecutor(req.Context(), db.si.GetInvalidationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{
"boundDN": req.BindDN,
"client": req.RemoteAddr(),
"requestId": req.ID(),
})
fe.SetSession(flags.Session)
fe.DelegateClientIP(req.RemoteAddr())
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
_, err := fe.Execute()
if err != nil {
db.log.WithError(err).Warning("failed to logout user")
}
db.si.SetFlags(req.BindDN, nil)
return ldap.LDAPResultSuccess, nil
}

View File

@ -27,10 +27,11 @@ type ProviderInstance struct {
searcher search.Searcher
binder bind.Binder
appSlug string
flowSlug string
s *LDAPServer
log *log.Entry
appSlug string
authenticationFlowSlug string
invalidationFlowSlug string
s *LDAPServer
log *log.Entry
tlsServerName *string
cert *tls.Certificate
@ -79,9 +80,13 @@ func (pi *ProviderInstance) GetFlags(dn string) *flags.UserFlags {
return flags
}
func (pi *ProviderInstance) SetFlags(dn string, flag flags.UserFlags) {
func (pi *ProviderInstance) SetFlags(dn string, flag *flags.UserFlags) {
pi.boundUsersMutex.Lock()
pi.boundUsers[dn] = &flag
if flag == nil {
delete(pi.boundUsers, dn)
} else {
pi.boundUsers[dn] = flag
}
pi.boundUsersMutex.Unlock()
}
@ -89,8 +94,12 @@ func (pi *ProviderInstance) GetAppSlug() string {
return pi.appSlug
}
func (pi *ProviderInstance) GetFlowSlug() string {
return pi.flowSlug
func (pi *ProviderInstance) GetAuthenticationFlowSlug() string {
return pi.authenticationFlowSlug
}
func (pi *ProviderInstance) GetInvalidationFlowSlug() string {
return pi.invalidationFlowSlug
}
func (pi *ProviderInstance) GetSearchAllowedGroups() []*strfmt.UUID {

View File

@ -1,6 +1,7 @@
package ldap
import (
"context"
"crypto/tls"
"net"
"sync"
@ -40,6 +41,7 @@ func NewServer(ac *ak.APIController) *LDAPServer {
}
ls.defaultCert = &defaultCert
s.BindFunc("", ls)
s.UnbindFunc("", ls)
s.SearchFunc("", ls)
return ls
}
@ -92,9 +94,13 @@ func (ls *LDAPServer) Start() error {
return nil
}
func (ls *LDAPServer) TimerFlowCacheExpiry() {
func (ls *LDAPServer) Stop() error {
return nil
}
func (ls *LDAPServer) TimerFlowCacheExpiry(ctx context.Context) {
for _, p := range ls.providers {
ls.log.WithField("flow", p.flowSlug).Debug("Pre-heating flow cache")
p.binder.TimerFlowCacheExpiry()
ls.log.WithField("flow", p.authenticationFlowSlug).Debug("Pre-heating flow cache")
p.binder.TimerFlowCacheExpiry(ctx)
}
}

View File

@ -28,6 +28,16 @@ func (ls *LDAPServer) getCurrentProvider(pk int32) *ProviderInstance {
return nil
}
func (ls *LDAPServer) getInvalidationFlow() string {
req, _, err := ls.ac.Client.CoreApi.CoreTenantsCurrentRetrieve(context.Background()).Execute()
if err != nil {
ls.log.WithError(err).Warning("failed to fetch tenant config")
return ""
}
flow := req.GetFlowInvalidation()
return flow
}
func (ls *LDAPServer) Refresh() error {
outposts, _, err := ls.ac.Client.OutpostsApi.OutpostsLdapList(context.Background()).Execute()
if err != nil {
@ -37,6 +47,7 @@ func (ls *LDAPServer) Refresh() error {
return errors.New("no ldap provider defined")
}
providers := make([]*ProviderInstance, len(outposts.Results))
invalidationFlow := ls.getInvalidationFlow()
for idx, provider := range outposts.Results {
userDN := strings.ToLower(fmt.Sprintf("ou=%s,%s", constants.OUUsers, *provider.BaseDn))
groupDN := strings.ToLower(fmt.Sprintf("ou=%s,%s", constants.OUGroups, *provider.BaseDn))
@ -53,22 +64,23 @@ func (ls *LDAPServer) Refresh() error {
}
providers[idx] = &ProviderInstance{
BaseDN: *provider.BaseDn,
VirtualGroupDN: virtualGroupDN,
GroupDN: groupDN,
UserDN: userDN,
appSlug: provider.ApplicationSlug,
flowSlug: provider.BindFlowSlug,
searchAllowedGroups: []*strfmt.UUID{(*strfmt.UUID)(provider.SearchGroup.Get())},
boundUsersMutex: sync.RWMutex{},
boundUsers: users,
s: ls,
log: logger,
tlsServerName: provider.TlsServerName,
uidStartNumber: *provider.UidStartNumber,
gidStartNumber: *provider.GidStartNumber,
outpostName: ls.ac.Outpost.Name,
outpostPk: provider.Pk,
BaseDN: *provider.BaseDn,
VirtualGroupDN: virtualGroupDN,
GroupDN: groupDN,
UserDN: userDN,
appSlug: provider.ApplicationSlug,
authenticationFlowSlug: provider.BindFlowSlug,
invalidationFlowSlug: invalidationFlow,
searchAllowedGroups: []*strfmt.UUID{(*strfmt.UUID)(provider.SearchGroup.Get())},
boundUsersMutex: sync.RWMutex{},
boundUsers: users,
s: ls,
log: logger,
tlsServerName: provider.TlsServerName,
uidStartNumber: *provider.UidStartNumber,
gidStartNumber: *provider.GidStartNumber,
outpostName: ls.ac.Outpost.Name,
outpostPk: provider.Pk,
}
if kp := provider.Certificate.Get(); kp != nil {
err := ls.cs.AddKeypair(*kp)

View File

@ -11,7 +11,8 @@ type LDAPServerInstance interface {
GetAPIClient() *api.APIClient
GetOutpostName() string
GetFlowSlug() string
GetAuthenticationFlowSlug() string
GetInvalidationFlowSlug() string
GetAppSlug() string
GetSearchAllowedGroups() []*strfmt.UUID
@ -32,7 +33,7 @@ type LDAPServerInstance interface {
UsersForGroup(api.Group) []string
GetFlags(dn string) *flags.UserFlags
SetFlags(dn string, flags flags.UserFlags)
SetFlags(dn string, flags *flags.UserFlags)
GetBaseEntry() *ldap.Entry
GetNeededObjects(int, string, string) (bool, bool)

View File

@ -0,0 +1,53 @@
package ldap
import (
"net"
"github.com/getsentry/sentry-go"
"github.com/nmcclain/ldap"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/outpost/ldap/bind"
"goauthentik.io/internal/outpost/ldap/metrics"
)
func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode, error) {
req, span := bind.NewRequest(boundDN, "", conn)
selectedApp := ""
defer func() {
span.Finish()
metrics.Requests.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "unbind",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Unbind request")
}()
defer func() {
err := recover()
if err == nil {
return
}
log.WithError(err.(error)).Error("recover in bind request")
sentry.CaptureException(err.(error))
}()
for _, instance := range ls.providers {
username, err := instance.binder.GetUsername(boundDN)
if err == nil {
selectedApp = instance.GetAppSlug()
return instance.binder.Unbind(username, req)
} else {
req.Log().WithError(err).Debug("Username not for instance")
}
}
req.Log().WithField("request", "unbind").Warning("No provider found for request")
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "unbind",
"reason": "no_provider",
"app": "",
}).Inc()
return ldap.LDAPResultOperationsError, nil
}

View File

@ -81,7 +81,7 @@ func (ps *ProxyServer) Type() string {
return "proxy"
}
func (ps *ProxyServer) TimerFlowCacheExpiry() {}
func (ps *ProxyServer) TimerFlowCacheExpiry(context.Context) {}
func (ps *ProxyServer) GetCertificate(serverName string) *tls.Certificate {
app, ok := ps.apps[serverName]
@ -163,6 +163,10 @@ func (ps *ProxyServer) Start() error {
return nil
}
func (ps *ProxyServer) Stop() error {
return nil
}
func (ps *ProxyServer) serve(listener net.Listener) {
srv := &http.Server{Handler: ps.mux}

View File

@ -61,7 +61,7 @@ func (ws *WebServer) configureStatic() {
func (ws *WebServer) staticHeaderMiddleware(h http.Handler) http.Handler {
etagHandler := etag.Handler(h, false)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "\"public, no-transform\"")
w.Header().Set("Cache-Control", "public, no-transform")
w.Header().Set("X-authentik-version", constants.VERSION)
w.Header().Set("Vary", "X-authentik-version, Etag")
etagHandler.ServeHTTP(w, r)

120
poetry.lock generated
View File

@ -789,63 +789,63 @@ files = [
[[package]]
name = "coverage"
version = "7.0.5"
version = "7.1.0"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"},
{file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"},
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"},
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"},
{file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"},
{file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"},
{file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"},
{file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"},
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"},
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"},
{file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"},
{file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"},
{file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"},
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"},
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"},
{file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"},
{file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"},
{file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"},
{file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"},
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"},
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"},
{file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"},
{file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"},
{file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"},
{file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"},
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"},
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"},
{file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"},
{file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"},
{file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"},
{file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"},
{file = "coverage-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf"},
{file = "coverage-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801"},
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75"},
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c"},
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada"},
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f"},
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a"},
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c"},
{file = "coverage-7.1.0-cp310-cp310-win32.whl", hash = "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352"},
{file = "coverage-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038"},
{file = "coverage-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040"},
{file = "coverage-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a"},
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f"},
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222"},
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146"},
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b"},
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"},
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e"},
{file = "coverage-7.1.0-cp311-cp311-win32.whl", hash = "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7"},
{file = "coverage-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c"},
{file = "coverage-7.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d"},
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a"},
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8"},
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050"},
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c"},
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d"},
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3"},
{file = "coverage-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73"},
{file = "coverage-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5"},
{file = "coverage-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06"},
{file = "coverage-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52"},
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851"},
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d"},
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0"},
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912"},
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8"},
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0"},
{file = "coverage-7.1.0-cp38-cp38-win32.whl", hash = "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab"},
{file = "coverage-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c"},
{file = "coverage-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6"},
{file = "coverage-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa"},
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc"},
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311"},
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063"},
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09"},
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8"},
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c"},
{file = "coverage-7.1.0-cp39-cp39-win32.whl", hash = "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4"},
{file = "coverage-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3"},
{file = "coverage-7.1.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda"},
{file = "coverage-7.1.0.tar.gz", hash = "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265"},
]
[package.extras]
@ -2851,14 +2851,14 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "selenium"
version = "4.7.2"
version = "4.8.0"
description = ""
category = "dev"
optional = false
python-versions = "~=3.7"
python-versions = ">=3.7"
files = [
{file = "selenium-4.7.2-py3-none-any.whl", hash = "sha256:06a1c7d9f313130b21c3218ddd8852070d0e7419afdd31f96160cd576555a5ce"},
{file = "selenium-4.7.2.tar.gz", hash = "sha256:3aefa14a28a42e520550c1cd0f29cf1d566328186ea63aa9a3e01fb265b5894d"},
{file = "selenium-4.8.0-py3-none-any.whl", hash = "sha256:20f28ee4ea9b273b4112a7df5276ebb3052f79ff6eff42a564db6143e5926683"},
{file = "selenium-4.8.0.tar.gz", hash = "sha256:fee36724d6cf0b18c73781bb8ec7be4a35ab1e2564e64e64e64da75e50e052af"},
]
[package.dependencies]
@ -2869,14 +2869,14 @@ urllib3 = {version = ">=1.26,<2.0", extras = ["socks"]}
[[package]]
name = "sentry-sdk"
version = "1.13.0"
version = "1.14.0"
description = "Python client for Sentry (https://sentry.io)"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "sentry-sdk-1.13.0.tar.gz", hash = "sha256:72da0766c3069a3941eadbdfa0996f83f5a33e55902a19ba399557cfee1dddcc"},
{file = "sentry_sdk-1.13.0-py2.py3-none-any.whl", hash = "sha256:b7ff6318183e551145b5c4766eb65b59ad5b63ff234dffddc5fb50340cad6729"},
{file = "sentry-sdk-1.14.0.tar.gz", hash = "sha256:273fe05adf052b40fd19f6d4b9a5556316807246bd817e5e3482930730726bb0"},
{file = "sentry_sdk-1.14.0-py2.py3-none-any.whl", hash = "sha256:72c00322217d813cf493fe76590b23a757e063ff62fec59299f4af7201dd4448"},
]
[package.dependencies]
@ -2894,7 +2894,7 @@ falcon = ["falcon (>=1.4)"]
fastapi = ["fastapi (>=0.79.0)"]
flask = ["blinker (>=1.1)", "flask (>=0.11)"]
httpx = ["httpx (>=0.16.0)"]
opentelemetry = ["opentelemetry-distro (>=0.350b0)"]
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
pure-eval = ["asttokens", "executing", "pure-eval"]
pymongo = ["pymongo (>=3.1)"]
pyspark = ["pyspark (>=2.4.4)"]

View File

@ -23008,6 +23008,10 @@ paths:
name: label
schema:
type: string
- in: query
name: name
schema:
type: string
- name: ordering
required: false
in: query
@ -34263,6 +34267,9 @@ components:
type: object
description: Prompt Serializer
properties:
name:
type: string
minLength: 1
field_key:
type: string
minLength: 1
@ -35278,6 +35285,8 @@ components:
format: uuid
readOnly: true
title: Prompt uuid
name:
type: string
field_key:
type: string
description: Name of the form field, also used to store the value
@ -35304,6 +35313,7 @@ components:
required:
- field_key
- label
- name
- pk
- type
PromptChallenge:
@ -35345,6 +35355,9 @@ components:
type: object
description: Prompt Serializer
properties:
name:
type: string
minLength: 1
field_key:
type: string
minLength: 1
@ -35373,6 +35386,7 @@ components:
required:
- field_key
- label
- name
- type
PromptStage:
type: object

View File

@ -26,8 +26,8 @@ class TestFlowsAuthenticator(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_totp_validate(self):
"""test flow with otp stages"""
@ -52,10 +52,10 @@ class TestFlowsAuthenticator(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint("default/20-flow-default-authenticator-totp-setup.yaml")
@apply_blueprint("default/flow-default-authenticator-totp-setup.yaml")
def test_totp_setup(self):
"""test TOTP Setup stage"""
flow: Flow = Flow.objects.get(slug="default-authentication-flow")
@ -98,10 +98,10 @@ class TestFlowsAuthenticator(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint("default/20-flow-default-authenticator-static-setup.yaml")
@apply_blueprint("default/flow-default-authenticator-static-setup.yaml")
def test_static_setup(self):
"""test Static OTP Setup stage"""
flow: Flow = Flow.objects.get(slug="default-authentication-flow")

View File

@ -41,19 +41,28 @@ class TestFlowsEnroll(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_enroll_2_step(self):
"""Test 2-step enroll flow"""
# First stage fields
username_prompt = Prompt.objects.create(
field_key="username", label="Username", order=0, type=FieldTypes.TEXT
name=generate_id(),
field_key="username",
label="Username",
order=0,
type=FieldTypes.TEXT,
)
password = Prompt.objects.create(
field_key="password", label="Password", order=1, type=FieldTypes.PASSWORD
name=generate_id(),
field_key="password",
label="Password",
order=1,
type=FieldTypes.PASSWORD,
)
password_repeat = Prompt.objects.create(
name=generate_id(),
field_key="password_repeat",
label="Password (repeat)",
order=2,
@ -62,10 +71,10 @@ class TestFlowsEnroll(SeleniumTestCase):
# Second stage fields
name_field = Prompt.objects.create(
field_key="name", label="Name", order=0, type=FieldTypes.TEXT
name=generate_id(), field_key="name", label="Name", order=0, type=FieldTypes.TEXT
)
email = Prompt.objects.create(
field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
name=generate_id(), field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
)
# Stages
@ -107,19 +116,28 @@ class TestFlowsEnroll(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_enroll_email(self):
"""Test enroll with Email verification"""
# First stage fields
username_prompt = Prompt.objects.create(
field_key="username", label="Username", order=0, type=FieldTypes.TEXT
name=generate_id(),
field_key="username",
label="Username",
order=0,
type=FieldTypes.TEXT,
)
password = Prompt.objects.create(
field_key="password", label="Password", order=1, type=FieldTypes.PASSWORD
name=generate_id(),
field_key="password",
label="Password",
order=1,
type=FieldTypes.PASSWORD,
)
password_repeat = Prompt.objects.create(
name=generate_id(),
field_key="password_repeat",
label="Password (repeat)",
order=2,
@ -128,10 +146,10 @@ class TestFlowsEnroll(SeleniumTestCase):
# Second stage fields
name_field = Prompt.objects.create(
field_key="name", label="Name", order=0, type=FieldTypes.TEXT
name=generate_id(), field_key="name", label="Name", order=0, type=FieldTypes.TEXT
)
email = Prompt.objects.create(
field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
name=generate_id(), field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
)
# Stages

View File

@ -12,8 +12,8 @@ class TestFlowsLogin(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_login(self):
"""test default login flow"""

View File

@ -18,10 +18,10 @@ class TestFlowsStageSetup(SeleniumTestCase):
"""test stage setup flows"""
@retry()
@apply_blueprint("default/0-flow-password-change.yaml")
@apply_blueprint("default/flow-password-change.yaml")
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_password_change(self):
"""test password change flow"""

View File

@ -81,8 +81,8 @@ class TestProviderLDAP(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_ldap_bind_success(self):
"""Test simple bind"""
@ -108,8 +108,8 @@ class TestProviderLDAP(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_ldap_bind_success_ssl(self):
"""Test simple bind with ssl"""
@ -135,8 +135,8 @@ class TestProviderLDAP(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_ldap_bind_fail(self):
"""Test simple bind (failed)"""
@ -160,8 +160,8 @@ class TestProviderLDAP(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@reconcile_app("authentik_outposts")
def test_ldap_bind_search(self):

View File

@ -58,12 +58,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -114,12 +114,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -189,12 +189,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
def test_denied(self):

View File

@ -67,12 +67,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -116,12 +116,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -178,12 +178,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -249,12 +249,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -329,12 +329,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",

View File

@ -65,12 +65,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
def test_redirect_uri_error(self):
@ -111,12 +111,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
@apply_blueprint("system/providers-oauth2.yaml")
@ -166,12 +166,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
@apply_blueprint("system/providers-oauth2.yaml")
@ -236,12 +236,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
def test_authorization_denied(self):

View File

@ -65,12 +65,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
def test_redirect_uri_error(self):
@ -111,12 +111,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
@apply_blueprint("system/providers-oauth2.yaml")
@ -161,12 +161,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
@apply_blueprint("system/providers-oauth2.yaml")
@ -227,12 +227,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
def test_authorization_denied(self):

View File

@ -57,12 +57,12 @@ class TestProviderProxy(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -123,12 +123,12 @@ class TestProviderProxy(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -200,12 +200,12 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@reconcile_app("authentik_crypto")
def test_proxy_connectivity(self):

View File

@ -64,12 +64,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -133,12 +133,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -217,12 +217,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -301,12 +301,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -376,12 +376,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -425,12 +425,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",

View File

@ -143,17 +143,17 @@ class TestSourceOAuth2(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-flow-default-source-pre-authentication.yaml",
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
)
def test_oauth_enroll(self):
"""test OAuth Source With With OIDC"""
@ -200,12 +200,12 @@ class TestSourceOAuth2(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
)
def test_oauth_enroll_auth(self):
"""test OAuth Source With With OIDC (enroll and authenticate again)"""
@ -292,13 +292,13 @@ class TestSourceOAuth1(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-flow-default-source-pre-authentication.yaml",
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
)
def test_oauth_enroll(self):
"""test OAuth Source With With OIDC"""

View File

@ -96,13 +96,13 @@ class TestSourceSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-flow-default-source-pre-authentication.yaml",
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
)
def test_idp_redirect(self):
"""test SAML Source With redirect binding"""
@ -166,13 +166,13 @@ class TestSourceSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-flow-default-source-pre-authentication.yaml",
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
)
def test_idp_post(self):
"""test SAML Source With post binding"""
@ -249,13 +249,13 @@ class TestSourceSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-flow-default-source-pre-authentication.yaml",
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
)
def test_idp_post_auto(self):
"""test SAML Source With post binding (auto redirect)"""

432
web/package-lock.json generated
View File

@ -21,13 +21,13 @@
"@codemirror/legacy-modes": "^6.3.1",
"@formatjs/intl-listformat": "^7.1.7",
"@fortawesome/fontawesome-free": "^6.2.1",
"@goauthentik/api": "^2023.1.0-1674058489",
"@goauthentik/api": "^2023.1.2-1674559422",
"@hcaptcha/types": "^1.0.3",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.16.0",
"@lingui/cli": "^3.16.1",
"@lingui/core": "^3.16.0",
"@lingui/detect-locale": "^3.16.0",
"@lingui/macro": "^3.16.0",
"@lingui/detect-locale": "^3.16.1",
"@lingui/macro": "^3.16.1",
"@patternfly/patternfly": "^4.222.4",
"@polymer/iron-form": "^3.0.1",
"@polymer/paper-input": "^3.2.1",
@ -36,15 +36,15 @@
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.0.0",
"@sentry/browser": "^7.31.1",
"@sentry/tracing": "^7.31.1",
"@sentry/browser": "^7.33.0",
"@sentry/tracing": "^7.33.0",
"@squoosh/cli": "^0.7.3",
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
"@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.6",
"@types/codemirror": "5.60.7",
"@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"@webcomponents/webcomponentsjs": "^2.7.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3",
@ -64,7 +64,7 @@
"mermaid": "^9.3.0",
"moment": "^2.29.4",
"prettier": "^2.8.3",
"pyright": "^1.1.290",
"pyright": "^1.1.291",
"rapidoc": "^9.3.4",
"rollup": "^2.79.1",
"rollup-plugin-copy": "^3.4.0",
@ -1961,9 +1961,9 @@
}
},
"node_modules/@goauthentik/api": {
"version": "2023.1.0-1674058489",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.0-1674058489.tgz",
"integrity": "sha512-k100Z1Tx4o7EsIq+tezavHSSddpr51NlGJZyR9QHsaQq6K+BpxZVcozR7xau7zK615JAwljWeG4yfEq9to++rw=="
"version": "2023.1.2-1674559422",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.2-1674559422.tgz",
"integrity": "sha512-zkbsIGy9J5QvtL70JH/svY0vGvbJhfYkhBxlt61dxCI3qDaof2RjJvm913OP6N/h8JoSzbyj8uZGJs4y8pHLhw=="
},
"node_modules/@hcaptcha/types": {
"version": "1.0.3",
@ -2215,13 +2215,13 @@
}
},
"node_modules/@lingui/babel-plugin-extract-messages": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.0.tgz",
"integrity": "sha512-Wxfp9cJfVjwhVigufIssoDntaTm4a8XXkmsV9gTMCr2xJL6ZowoC80TltBLOu0Dt829e6pdy+TFuwuKwAvnmrA==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.1.tgz",
"integrity": "sha512-ipt2WDGzSs0zsZ3BysFkDzz/LjUxSx5xh6/ScbXqcYDZc/b0jL87cj9S9csZdsoXLWWfeaPHvIgD5yTSthkM6w==",
"dependencies": {
"@babel/generator": "^7.11.6",
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.0",
"@lingui/conf": "3.16.1",
"mkdirp": "^1.0.4"
},
"engines": {
@ -2229,18 +2229,18 @@
}
},
"node_modules/@lingui/cli": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.0.tgz",
"integrity": "sha512-6sUVpA6UB4BwNtLjC9aG62QvxkhHwVmDOi9vO8kb9W4SKPBaMdHuaxEsVVBuD1ZWcFZULcNUmPh9kKAnMRkHHw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.1.tgz",
"integrity": "sha512-20IezHftBqJe8EYls+fwftni5f2dBWFe+d2J7a6DRKuvuSxBD2UforLPna9dq1Ya84MnZGQBhhY4zSXIXvO+fA==",
"dependencies": {
"@babel/generator": "^7.11.6",
"@babel/parser": "^7.11.5",
"@babel/plugin-syntax-jsx": "^7.10.4",
"@babel/runtime": "^7.11.2",
"@babel/types": "^7.11.5",
"@lingui/babel-plugin-extract-messages": "3.16.0",
"@lingui/conf": "3.16.0",
"@lingui/core": "3.16.0",
"@lingui/babel-plugin-extract-messages": "3.16.1",
"@lingui/conf": "3.16.1",
"@lingui/core": "3.16.1",
"babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7",
"chalk": "^4.1.0",
@ -2342,9 +2342,9 @@
}
},
"node_modules/@lingui/conf": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.0.tgz",
"integrity": "sha512-65X3TySGzeYjVNE3YZDpF6LTYxpUiSRSyTJjEGB7ZSPpuL6VDMEUGCDsnur2ix8D+I8w0ife6ks4HPQt6owZmw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.1.tgz",
"integrity": "sha512-F8aYpjFXItmJLm09BxDzUZTsK0bB7nxLpMkUmiawVuuyB+66mBanOQ1nQEn8+SdJHCqJo9sI1ZSg+1uFequGzg==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"chalk": "^4.1.0",
@ -2422,9 +2422,9 @@
}
},
"node_modules/@lingui/core": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.0.tgz",
"integrity": "sha512-2uZvxHv4IWF7xIRG1o4oXDFCrAhE0945Ses1eALmv/NqQ8BslXWWSq0Zf51qt+ZqQ3RfzCdl4kslZEqdGRe0gw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.1.tgz",
"integrity": "sha512-F/oos3IquuA71eVUfVBFY4teq9QuE3enGV65AMb9VB0vUTxsy6HAOIo1A2aZ4wJ0ST8V7FhYshYRU6qXguRjMQ==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@messageformat/parser": "^5.0.0",
@ -2435,20 +2435,20 @@
}
},
"node_modules/@lingui/detect-locale": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.0.tgz",
"integrity": "sha512-7FQy1ccPnVe0/GNAzTmEDrsPHZV1/nSsK6fRKyuOFUkiItc1bVDmlpVfM4UXerERx6Nez+N99Z6Vjc6rg3LARw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.1.tgz",
"integrity": "sha512-zDm9grmwPjQ5FXoIpkt0Fvq2bH+d01lI7zTmNFdTcM4IK4vsgAYqSY2h94W+7A3vyHEeaIULm5XtLBePSLJLcQ==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@lingui/macro": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.0.tgz",
"integrity": "sha512-3HfP1Bqr4i60P3LoQq/ukhDvel4a5oBSMPRuYBUpwqdnKOAbZuN5vnZmu3TrlXntWTYOvrwa71D8SYGmvevelw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.1.tgz",
"integrity": "sha512-CmR6u37Wzb+P4FGJJtbjY3SSX6vQb1IOj3Wcv9DYKvi1nepMvtvc5hcgCQKmqTdDi29fx6re5lmfG8YnthLkwA==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.0",
"@lingui/conf": "3.16.1",
"ramda": "^0.27.1"
},
"engines": {
@ -2974,14 +2974,14 @@
}
},
"node_modules/@sentry/browser": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.31.1.tgz",
"integrity": "sha512-Rg9F61S1tz1Dv3iUyyGP26bxoi7WJAG2+f2fBbSmFuJ+JTH4Jvu2/F1bBig8Dz01ejzVhbNSUUCfoDhSvksIsQ==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.33.0.tgz",
"integrity": "sha512-bvExBTyLb7cLWLkHh0gch2W/oSw08Yo8DgEc+KkikOnvWd/xoEWUsYNydYGzV+bL1jqcOErsZy0fVsbzTmh71g==",
"dependencies": {
"@sentry/core": "7.31.1",
"@sentry/replay": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"@sentry/core": "7.33.0",
"@sentry/replay": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2994,12 +2994,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/core": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.31.1.tgz",
"integrity": "sha512-quaNU6z8jabmatBTDi28Wpff2yzfWIp/IU4bbi2QOtEiCNT+TQJXqlRTRMu9xLrX7YzyKCL5X2gbit/85lyWUg==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.33.0.tgz",
"integrity": "sha512-mrSgUnXjxHVi0cVea1lv7gC/Y66ya2a3atCHaPEij/+l+3APg5d0Ixt1zMx5YllMiZKf6wpxlZ0uwXcqdAAw+w==",
"dependencies": {
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3012,26 +3012,26 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/replay": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.31.1.tgz",
"integrity": "sha512-sLArvwZn6IwA/bASctyhxN7LhdCXJvMmyTynRfmk7pzuNzBMc5CNlHeIsDpHrfQuH53IKicvl6cHnHyclu5DSA==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.33.0.tgz",
"integrity": "sha512-m6xpSdjsNCCGxAkk5ikPFv/sQAfWtieMEXLdeDZE9jnroVozweHpsUhZYhqzTpxVC5SA3jPyTQ6Ods5gRvTBfA==",
"dependencies": {
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1"
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@sentry/tracing": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.31.1.tgz",
"integrity": "sha512-kW6vNwddp2Ycq2JfTzveUEIRF9YQwvl7L6BBoOZt9oVnYlsPipEeyU2Q277LatHldr8hDo2tbz/vz2BQjO5GSw==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.33.0.tgz",
"integrity": "sha512-MtcKyW/QJgXGrHf5+205xnIIl7yIT99MzuTkuKzQwmnmy/siD3U0X8RoCaGLzj6kkSIu4m7vyQZoyd3J+5D8lw==",
"dependencies": {
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3044,19 +3044,19 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/types": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.31.1.tgz",
"integrity": "sha512-1uzr2l0AxEnxUX/S0EdmXUQ15/kDsam8Nbdw4Gai8SU764XwQgA/TTjoewVP597CDI/AHKan67Y630/Ylmkx9w==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.33.0.tgz",
"integrity": "sha512-5kkmYjtBWSbPxfYGiXdZFPS6xpFBNlXvDqeX4NpCFXz6/LiEDn6tZ61kuCSFb8MZlyqyCX5WsP3aiI2FJfpGIA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/utils": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.31.1.tgz",
"integrity": "sha512-ZsIPq29aNdP9q3R7qIzJhZ9WW+4DzE9g5SfGwx3UjTIxoRRBfdUJUbf7S+LKEdvCkKbyoDt6FLt5MiSJV43xBA==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.33.0.tgz",
"integrity": "sha512-msp02GV1gOfaN5FjKjWxI00rtbYLXEE5cTGldhs/Dt9KI63dDk1nwPDkSLhg6joqRItAq0thlBh6un717HdWbg==",
"dependencies": {
"@sentry/types": "7.31.1",
"@sentry/types": "7.33.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3256,9 +3256,9 @@
}
},
"node_modules/@types/codemirror": {
"version": "5.60.6",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz",
"integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==",
"version": "5.60.7",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz",
"integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==",
"dependencies": {
"@types/tern": "*"
}
@ -3404,13 +3404,13 @@
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz",
"integrity": "sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
"dependencies": {
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/type-utils": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/type-utils": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"debug": "^4.3.4",
"ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0",
@ -3450,13 +3450,13 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
"dependencies": {
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"debug": "^4.3.4"
},
"engines": {
@ -3476,12 +3476,12 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz",
"integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
"dependencies": {
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2"
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -3492,12 +3492,12 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz",
"integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
"dependencies": {
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
@ -3518,9 +3518,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@ -3530,12 +3530,12 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
"dependencies": {
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -3570,15 +3570,15 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz",
"integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
"dependencies": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
@ -3609,11 +3609,11 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz",
"integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
"dependencies": {
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/types": "5.49.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@ -8170,9 +8170,9 @@
}
},
"node_modules/pyright": {
"version": "1.1.290",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.290.tgz",
"integrity": "sha512-iHT8G2+R/GUfYWxxd4ud5Lj/0H0bE1eWVR0avKKgPtkBeLkUz3sorjKpfuaJdsuJNrunzMtZsmYPD366t39klg==",
"version": "1.1.291",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.291.tgz",
"integrity": "sha512-wkSlCEhF1OwtJqHuifwxk76UMr1NPqsAbPIlCh7tiZx8VUdZs17NCGNorhyb2M+GFEvp8pod9Xyu1LXN1JL2kQ==",
"bin": {
"pyright": "index.js",
"pyright-langserver": "langserver.index.js"
@ -11437,9 +11437,9 @@
"integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A=="
},
"@goauthentik/api": {
"version": "2023.1.0-1674058489",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.0-1674058489.tgz",
"integrity": "sha512-k100Z1Tx4o7EsIq+tezavHSSddpr51NlGJZyR9QHsaQq6K+BpxZVcozR7xau7zK615JAwljWeG4yfEq9to++rw=="
"version": "2023.1.2-1674559422",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.2-1674559422.tgz",
"integrity": "sha512-zkbsIGy9J5QvtL70JH/svY0vGvbJhfYkhBxlt61dxCI3qDaof2RjJvm913OP6N/h8JoSzbyj8uZGJs4y8pHLhw=="
},
"@hcaptcha/types": {
"version": "1.0.3",
@ -11650,29 +11650,29 @@
}
},
"@lingui/babel-plugin-extract-messages": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.0.tgz",
"integrity": "sha512-Wxfp9cJfVjwhVigufIssoDntaTm4a8XXkmsV9gTMCr2xJL6ZowoC80TltBLOu0Dt829e6pdy+TFuwuKwAvnmrA==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.1.tgz",
"integrity": "sha512-ipt2WDGzSs0zsZ3BysFkDzz/LjUxSx5xh6/ScbXqcYDZc/b0jL87cj9S9csZdsoXLWWfeaPHvIgD5yTSthkM6w==",
"requires": {
"@babel/generator": "^7.11.6",
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.0",
"@lingui/conf": "3.16.1",
"mkdirp": "^1.0.4"
}
},
"@lingui/cli": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.0.tgz",
"integrity": "sha512-6sUVpA6UB4BwNtLjC9aG62QvxkhHwVmDOi9vO8kb9W4SKPBaMdHuaxEsVVBuD1ZWcFZULcNUmPh9kKAnMRkHHw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.1.tgz",
"integrity": "sha512-20IezHftBqJe8EYls+fwftni5f2dBWFe+d2J7a6DRKuvuSxBD2UforLPna9dq1Ya84MnZGQBhhY4zSXIXvO+fA==",
"requires": {
"@babel/generator": "^7.11.6",
"@babel/parser": "^7.11.5",
"@babel/plugin-syntax-jsx": "^7.10.4",
"@babel/runtime": "^7.11.2",
"@babel/types": "^7.11.5",
"@lingui/babel-plugin-extract-messages": "3.16.0",
"@lingui/conf": "3.16.0",
"@lingui/core": "3.16.0",
"@lingui/babel-plugin-extract-messages": "3.16.1",
"@lingui/conf": "3.16.1",
"@lingui/core": "3.16.1",
"babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7",
"chalk": "^4.1.0",
@ -11744,9 +11744,9 @@
}
},
"@lingui/conf": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.0.tgz",
"integrity": "sha512-65X3TySGzeYjVNE3YZDpF6LTYxpUiSRSyTJjEGB7ZSPpuL6VDMEUGCDsnur2ix8D+I8w0ife6ks4HPQt6owZmw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.1.tgz",
"integrity": "sha512-F8aYpjFXItmJLm09BxDzUZTsK0bB7nxLpMkUmiawVuuyB+66mBanOQ1nQEn8+SdJHCqJo9sI1ZSg+1uFequGzg==",
"requires": {
"@babel/runtime": "^7.11.2",
"chalk": "^4.1.0",
@ -11802,9 +11802,9 @@
}
},
"@lingui/core": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.0.tgz",
"integrity": "sha512-2uZvxHv4IWF7xIRG1o4oXDFCrAhE0945Ses1eALmv/NqQ8BslXWWSq0Zf51qt+ZqQ3RfzCdl4kslZEqdGRe0gw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.1.tgz",
"integrity": "sha512-F/oos3IquuA71eVUfVBFY4teq9QuE3enGV65AMb9VB0vUTxsy6HAOIo1A2aZ4wJ0ST8V7FhYshYRU6qXguRjMQ==",
"requires": {
"@babel/runtime": "^7.11.2",
"@messageformat/parser": "^5.0.0",
@ -11812,17 +11812,17 @@
}
},
"@lingui/detect-locale": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.0.tgz",
"integrity": "sha512-7FQy1ccPnVe0/GNAzTmEDrsPHZV1/nSsK6fRKyuOFUkiItc1bVDmlpVfM4UXerERx6Nez+N99Z6Vjc6rg3LARw=="
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.1.tgz",
"integrity": "sha512-zDm9grmwPjQ5FXoIpkt0Fvq2bH+d01lI7zTmNFdTcM4IK4vsgAYqSY2h94W+7A3vyHEeaIULm5XtLBePSLJLcQ=="
},
"@lingui/macro": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.0.tgz",
"integrity": "sha512-3HfP1Bqr4i60P3LoQq/ukhDvel4a5oBSMPRuYBUpwqdnKOAbZuN5vnZmu3TrlXntWTYOvrwa71D8SYGmvevelw==",
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.1.tgz",
"integrity": "sha512-CmR6u37Wzb+P4FGJJtbjY3SSX6vQb1IOj3Wcv9DYKvi1nepMvtvc5hcgCQKmqTdDi29fx6re5lmfG8YnthLkwA==",
"requires": {
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.0",
"@lingui/conf": "3.16.1",
"ramda": "^0.27.1"
}
},
@ -12194,14 +12194,14 @@
}
},
"@sentry/browser": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.31.1.tgz",
"integrity": "sha512-Rg9F61S1tz1Dv3iUyyGP26bxoi7WJAG2+f2fBbSmFuJ+JTH4Jvu2/F1bBig8Dz01ejzVhbNSUUCfoDhSvksIsQ==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.33.0.tgz",
"integrity": "sha512-bvExBTyLb7cLWLkHh0gch2W/oSw08Yo8DgEc+KkikOnvWd/xoEWUsYNydYGzV+bL1jqcOErsZy0fVsbzTmh71g==",
"requires": {
"@sentry/core": "7.31.1",
"@sentry/replay": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"@sentry/core": "7.33.0",
"@sentry/replay": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12213,12 +12213,12 @@
}
},
"@sentry/core": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.31.1.tgz",
"integrity": "sha512-quaNU6z8jabmatBTDi28Wpff2yzfWIp/IU4bbi2QOtEiCNT+TQJXqlRTRMu9xLrX7YzyKCL5X2gbit/85lyWUg==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.33.0.tgz",
"integrity": "sha512-mrSgUnXjxHVi0cVea1lv7gC/Y66ya2a3atCHaPEij/+l+3APg5d0Ixt1zMx5YllMiZKf6wpxlZ0uwXcqdAAw+w==",
"requires": {
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12230,23 +12230,23 @@
}
},
"@sentry/replay": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.31.1.tgz",
"integrity": "sha512-sLArvwZn6IwA/bASctyhxN7LhdCXJvMmyTynRfmk7pzuNzBMc5CNlHeIsDpHrfQuH53IKicvl6cHnHyclu5DSA==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.33.0.tgz",
"integrity": "sha512-m6xpSdjsNCCGxAkk5ikPFv/sQAfWtieMEXLdeDZE9jnroVozweHpsUhZYhqzTpxVC5SA3jPyTQ6Ods5gRvTBfA==",
"requires": {
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1"
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0"
}
},
"@sentry/tracing": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.31.1.tgz",
"integrity": "sha512-kW6vNwddp2Ycq2JfTzveUEIRF9YQwvl7L6BBoOZt9oVnYlsPipEeyU2Q277LatHldr8hDo2tbz/vz2BQjO5GSw==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.33.0.tgz",
"integrity": "sha512-MtcKyW/QJgXGrHf5+205xnIIl7yIT99MzuTkuKzQwmnmy/siD3U0X8RoCaGLzj6kkSIu4m7vyQZoyd3J+5D8lw==",
"requires": {
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12258,16 +12258,16 @@
}
},
"@sentry/types": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.31.1.tgz",
"integrity": "sha512-1uzr2l0AxEnxUX/S0EdmXUQ15/kDsam8Nbdw4Gai8SU764XwQgA/TTjoewVP597CDI/AHKan67Y630/Ylmkx9w=="
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.33.0.tgz",
"integrity": "sha512-5kkmYjtBWSbPxfYGiXdZFPS6xpFBNlXvDqeX4NpCFXz6/LiEDn6tZ61kuCSFb8MZlyqyCX5WsP3aiI2FJfpGIA=="
},
"@sentry/utils": {
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.31.1.tgz",
"integrity": "sha512-ZsIPq29aNdP9q3R7qIzJhZ9WW+4DzE9g5SfGwx3UjTIxoRRBfdUJUbf7S+LKEdvCkKbyoDt6FLt5MiSJV43xBA==",
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.33.0.tgz",
"integrity": "sha512-msp02GV1gOfaN5FjKjWxI00rtbYLXEE5cTGldhs/Dt9KI63dDk1nwPDkSLhg6joqRItAq0thlBh6un717HdWbg==",
"requires": {
"@sentry/types": "7.31.1",
"@sentry/types": "7.33.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12430,9 +12430,9 @@
}
},
"@types/codemirror": {
"version": "5.60.6",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz",
"integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==",
"version": "5.60.7",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz",
"integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==",
"requires": {
"@types/tern": "*"
}
@ -12577,13 +12577,13 @@
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
"@typescript-eslint/eslint-plugin": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz",
"integrity": "sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
"requires": {
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/type-utils": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/type-utils": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"debug": "^4.3.4",
"ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0",
@ -12603,48 +12603,48 @@
}
},
"@typescript-eslint/parser": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
"requires": {
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz",
"integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
"requires": {
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2"
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0"
}
},
"@typescript-eslint/type-utils": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz",
"integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
"requires": {
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/types": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA=="
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg=="
},
"@typescript-eslint/typescript-estree": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
"requires": {
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -12663,15 +12663,15 @@
}
},
"@typescript-eslint/utils": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz",
"integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
"requires": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
@ -12688,11 +12688,11 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz",
"integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==",
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
"requires": {
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/types": "5.49.0",
"eslint-visitor-keys": "^3.3.0"
},
"dependencies": {
@ -16055,9 +16055,9 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"pyright": {
"version": "1.1.290",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.290.tgz",
"integrity": "sha512-iHT8G2+R/GUfYWxxd4ud5Lj/0H0bE1eWVR0avKKgPtkBeLkUz3sorjKpfuaJdsuJNrunzMtZsmYPD366t39klg=="
"version": "1.1.291",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.291.tgz",
"integrity": "sha512-wkSlCEhF1OwtJqHuifwxk76UMr1NPqsAbPIlCh7tiZx8VUdZs17NCGNorhyb2M+GFEvp8pod9Xyu1LXN1JL2kQ=="
},
"qrjs": {
"version": "0.1.2",

View File

@ -65,13 +65,13 @@
"@codemirror/legacy-modes": "^6.3.1",
"@formatjs/intl-listformat": "^7.1.7",
"@fortawesome/fontawesome-free": "^6.2.1",
"@goauthentik/api": "^2023.1.0-1674058489",
"@goauthentik/api": "^2023.1.2-1674559422",
"@hcaptcha/types": "^1.0.3",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.16.0",
"@lingui/cli": "^3.16.1",
"@lingui/core": "^3.16.0",
"@lingui/detect-locale": "^3.16.0",
"@lingui/macro": "^3.16.0",
"@lingui/detect-locale": "^3.16.1",
"@lingui/macro": "^3.16.1",
"@patternfly/patternfly": "^4.222.4",
"@polymer/iron-form": "^3.0.1",
"@polymer/paper-input": "^3.2.1",
@ -80,15 +80,15 @@
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.0.0",
"@sentry/browser": "^7.31.1",
"@sentry/tracing": "^7.31.1",
"@sentry/browser": "^7.33.0",
"@sentry/tracing": "^7.33.0",
"@squoosh/cli": "^0.7.3",
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
"@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.6",
"@types/codemirror": "5.60.7",
"@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"@webcomponents/webcomponentsjs": "^2.7.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3",
@ -108,7 +108,7 @@
"mermaid": "^9.3.0",
"moment": "^2.29.4",
"prettier": "^2.8.3",
"pyright": "^1.1.290",
"pyright": "^1.1.291",
"rapidoc": "^9.3.4",
"rollup": "^2.79.1",
"rollup-plugin-copy": "^3.4.0",

View File

@ -13,9 +13,11 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
import { t } from "@lingui/macro";
import { TemplateResult, html } from "lit";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import { BlueprintInstance, BlueprintInstanceStatusEnum, ManagedApi } from "@goauthentik/api";
export function BlueprintStatus(blueprint?: BlueprintInstance): string {
@ -32,6 +34,7 @@ export function BlueprintStatus(blueprint?: BlueprintInstance): string {
}
return t`Unknown`;
}
@customElement("ak-blueprint-list")
export class BlueprintListPage extends TablePage<BlueprintInstance> {
searchEnabled(): boolean {
@ -47,11 +50,16 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
return "pf-icon pf-icon-blueprint";
}
expandable = true;
checkbox = true;
@property()
order = "name";
static get styles(): CSSResult[] {
return super.styles.concat(PFDescriptionList);
}
async apiEndpoint(page: number): Promise<PaginatedResponse<BlueprintInstance>> {
return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsList({
ordering: this.order,
@ -96,9 +104,34 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
</ak-forms-delete-bulk>`;
}
renderExpanded(item: BlueprintInstance): TemplateResult {
return html`<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<dl class="pf-c-description-list pf-m-horizontal">
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${t`Path`}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<pre>${item.path}</pre>
</div>
</dd>
</div>
</dl>
</div>
</td>`;
}
row(item: BlueprintInstance): TemplateResult[] {
let description = undefined;
const descKey = "blueprints.goauthentik.io/description";
if (Object.hasOwn(item.metadata.labels, descKey)) {
description = item.metadata.labels[descKey];
}
return [
html`${item.name}`,
html`<div>${item.name}</div>
${description ? html`<small>${description}</small>` : html``}`,
html`${BlueprintStatus(item)}`,
html`${item.lastApplied.toLocaleString()}`,
html`<ak-label color=${item.enabled ? PFColor.Green : PFColor.Red}>

View File

@ -132,6 +132,17 @@ export class PromptForm extends ModelForm<Prompt, string> {
renderForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal">
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
<input
type="text"
value="${ifDefined(this.instance?.name)}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${t`Unique name of this field, used for selecting fields in prompt stages.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${t`Field Key`} ?required=${true} name="fieldKey">
<input
type="text"

View File

@ -1,7 +1,6 @@
import "@goauthentik/admin/stages/prompt/PromptForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { uiConfig } from "@goauthentik/common/ui/config";
import { truncate } from "@goauthentik/common/utils";
import "@goauthentik/elements/buttons/ModalButton";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/DeleteBulkForm";
@ -35,7 +34,7 @@ export class PromptListPage extends TablePage<Prompt> {
checkbox = true;
@property()
order = "order";
order = "name";
async apiEndpoint(page: number): Promise<PaginatedResponse<Prompt>> {
return new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsList({
@ -48,8 +47,8 @@ export class PromptListPage extends TablePage<Prompt> {
columns(): TableColumn[] {
return [
new TableColumn(t`Name`, "name"),
new TableColumn(t`Field`, "field_key"),
new TableColumn(t`Label`, "label"),
new TableColumn(t`Type`, "type"),
new TableColumn(t`Order`, "order"),
new TableColumn(t`Stages`),
@ -81,8 +80,8 @@ export class PromptListPage extends TablePage<Prompt> {
row(item: Prompt): TemplateResult[] {
return [
html`${item.fieldKey}`,
html`${truncate(item.label, 20)}`,
html`${item.name}`,
html`<code>${item.fieldKey}</code>`,
html`${item.type}`,
html`${item.order}`,
html`${item.promptstageSet?.map((stage) => {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -27,7 +27,7 @@ import "@goauthentik/flow/stages/password/PasswordStage";
import { t } from "@lingui/macro";
import { CSSResult, TemplateResult, css, html, render } from "lit";
import { customElement, property } from "lit/decorators.js";
import { customElement, property, state } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { until } from "lit/directives/until.js";
@ -44,6 +44,7 @@ import {
CapabilitiesEnum,
ChallengeChoices,
ChallengeTypes,
ContextualFlowInfo,
CurrentTenant,
FlowChallengeResponseRequest,
FlowErrorChallenge,
@ -95,9 +96,28 @@ export class FlowExecutor extends AKElement implements StageHost {
@property({ attribute: false })
tenant!: CurrentTenant;
@property({ attribute: false })
@state()
inspectorOpen: boolean;
_flowInfo?: ContextualFlowInfo;
@state()
set flowInfo(value: ContextualFlowInfo | undefined) {
this._flowInfo = value;
if (!value) {
return;
}
this.shadowRoot
?.querySelectorAll<HTMLDivElement>(".pf-c-background-image")
.forEach((bg) => {
bg.style.setProperty("--ak-flow-background", `url('${value?.background}')`);
});
}
get flowInfo(): ContextualFlowInfo | undefined {
return this._flowInfo;
}
ws: WebsocketClient;
static get styles(): CSSResult[] {
@ -168,14 +188,6 @@ export class FlowExecutor extends AKElement implements StageHost {
tenant().then((tenant) => (this.tenant = tenant));
}
setBackground(url: string): void {
this.shadowRoot
?.querySelectorAll<HTMLDivElement>(".pf-c-background-image")
.forEach((bg) => {
bg.style.setProperty("--ak-flow-background", `url('${url}')`);
});
}
submit(payload?: FlowChallengeResponseRequest): Promise<boolean> {
if (!payload) return Promise.reject();
if (!this.challenge) return Promise.reject();
@ -198,6 +210,9 @@ export class FlowExecutor extends AKElement implements StageHost {
);
}
this.challenge = data;
if (this.challenge.flowInfo) {
this.flowInfo = this.challenge.flowInfo;
}
if (this.challenge.responseErrors) {
return false;
}
@ -231,9 +246,8 @@ export class FlowExecutor extends AKElement implements StageHost {
);
}
this.challenge = challenge;
// Only set background on first update, flow won't change throughout execution
if (this.challenge?.flowInfo?.background) {
this.setBackground(this.challenge.flowInfo.background);
if (this.challenge.flowInfo) {
this.flowInfo = this.challenge.flowInfo;
}
})
.catch((e: Error | ResponseError) => {
@ -531,13 +545,11 @@ export class FlowExecutor extends AKElement implements StageHost {
>${t`Powered by authentik`}</a
>
</li>
${this.challenge?.flowInfo?.background?.startsWith(
"/static",
)
${this.flowInfo?.background?.startsWith("/static")
? html`
<li>
<a
href="https://unsplash.com/@chrishenryphoto"
href="https://unsplash.com/@saishmenon"
>${t`Background image`}</a
>
</li>

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View File

@ -0,0 +1,151 @@
---
title: "SaaS should not be the default: Why data breaches signal return to self-hosting"
description: More companies are realizing that SaaS isnt, and shouldnt be, the default.
slug: 2023-01-24-saas-should-not-be-the-default
authors:
- name: Nick Moore
title: Freelance writer
url: https://nickmoore.me/
image_url: https://nickmoore.me/assets/images/image01.jpg
tags: [sso, self-hosting]
hide_table_of_contents: false
---
“We made a mistake” so said authentication provider [Okta](https://support.okta.com/help/s/article/Frequently-Asked-Questions-Regarding-January-2022-Compromise?language=en_US) on March 25, 2022 two months after an attack on one of Oktas vendors (Sitel, a contact center) in January. During Oktas initial investigation, the company didnt warn its customers about the attack nor about its potential damage.
“At that time,” Okta admitted later, “We didnt recognize that there was a risk to Okta and our customers.”
On March 22, three days before the admission, the group responsible for the attack LAPSUS$ shared screenshots online that evidenced the success of their attack. As users, customers, and onlookers reacted, Okta co-founder and CEO Todd McKinnon [tweeted about the attack](https://twitter.com/toddmckinnon/status/1506184721922859010?s=20&t=o7e6RA25El2IEd7EMQD3Xg), claiming that the attack was “investigated and contained” but, more controversially, framing the attack as “an attempt.”
<!--truncate-->
Many disagreed with that framing considering, as the news progressed, that the attack had succeeded and had affected 2.5% of Okta customers ([about 375 companies](https://www.bleepingcomputer.com/news/security/okta-confirms-25-percent-customers-impacted-by-hack-in-january/)). Worse, LAPSUS$ itself disagreed, claiming they had “logged in to a superuser portal with the ability to reset the Password and MFA of ~95% of clients.”
Data breaches are not uncommon but in this case, the coverup became worse than the crime. In the days and weeks after, most criticism of Okta didnt focus on the attack itself but on the companys response. Okta had two months to talk about the attack before LAPSUS$ forced them to and its unclear whether Okta ever would have talked about it at all without the circulation of those screenshots.
Eventually, Okta admitted its faults. On March 23, David Bradbury, Chief Security Officer at Okta, [wrote that](https://www.okta.com/blog/2022/03/oktas-investigation-of-the-january-2022-compromise/): “I am greatly disappointed by the long period of time that transpired between our notification to Sitel and the issuance of the complete investigation report.”
The Okta case is one example in a line of many. Its a particularly galling case because Okta manages authentication for so many companies making it a frontline security product but the pattern itself is not rare.
A major consequence of the rise of SaaS software is a misalignment of incentives between SaaS vendors and customers. We dont have to put on tinfoil hats to realize that vendors have a strong incentive to ignore or even suppress bad news so as to safeguard their relationships with current and future customers.
As honest and as well-intentioned as a vendor might be, that incentive misalignment is still there. This tension exposes the leading edge of an emerging trend and potentially major shift: Companies are reconsidering the value of self-hosting their software so as to have greater control over security and cost.
### 5 incentives SaaS vendors have to be secretive about security
This is not a secret nor a conspiracy theory: SaaS vendors have a compelling array of incentives to hide security flaws in their services and suppress the publicity of successful data breaches.
The very model of delivering software as a service means that vendors are incentivized to maintain relationships with their customers so as to encourage them to maintain their subscriptions. That incentive leads to good things, such as prompt customer service and product iteration. But it can also lead to bad things, such as hiding mistakes and flaws.
Its hard, bordering on impossible, to claim that any given company suppressed news about a data breach. But we can infer its likely that it happens given three things:
- The SaaS industry is [massive and growing](https://www.grandviewresearch.com/industry-analysis/saas-market-report), meaning there are many companies out there that _could_ suffer a data breach and _could_ suppress news about it.
- The media industry is inherently limited and cant discover and report on every data breach.
- The number of data breaches has [consistently risen](https://www.statista.com/statistics/273550/data-breaches-recorded-in-the-united-states-by-number-of-breaches-and-records-exposed/) from 2005 to 2021.
Given these three dynamics, its likely some significant portion of vendors have tried, or at least hoped, for news about a data breach to not break headlines. Is it ethical? Likely not. But is it rewarding? If it all works out, yes. Lets look, then, at the five biggest incentives companies have to suppress data breach news.
#### 1. Fines
With the passing of the General Data Protection Regulation (GDPR) in Europe, along with a slew of other regulations, many of which are still emerging, fines have become a significant concern for companies.
GDPR fines are designed, in the [words of the EU](https://gdpr.eu/fines/), to “make non-compliance a costly mistake for both large and small businesses.”
The “less severe infringements” can cost companies up to €10 million (almost $11 million) or up to 2% of the companys annual revenue ”whichever amount is _higher_” [emphasis ours]. The “more serious infringements” can cost companies €20 million (about $21.5 million) or 4% of the companys annual revenue again, “whichever amount is higher.”
#### 2. Reputation
At first glance, the reputation cost of a data breach might seem minimal. Even headline-breaking data breaches dont always seem to impair companies.
You couldnt infer, for example, when the infamous Experian data breach occurred looking at its stock price alone.
![alt_text](./image2.png "image_tooltip")
(It happened in September of 2017 and a [class action lawsuit](https://www.ftc.gov/enforcement/refunds/equifax-data-breach-settlement) resulted in payments starting in December of 2022).
The problem with considering the potential of reputation damage is that its hard to predict. There are a few factors that make news coverage of a data breach more likely, such as whether a company targets average users or business users and whether a company stores obviously sensitive data or not, but predictability remains hard.
Your company neednt trend on Twitter to suffer reputation damage, however. According to [Impravata research](https://security.imprivata.com/rs/413-FZZ-310/images/IM_Report_Third-Party-Remote-Access-Security.pdf), 63% of companies dont do security evaluations on prospective vendors because they rely instead on the reputation of the vendors in question.
The incentive to suppress bad news and avoid a bad reputation also worsens with time. The same research shows that 55% of companies consider a “history of _frequent_ data breach incidents” [emphasis ours] to be a major indicator of risk. That means a company might be transparent about it first breach and gradually more secretive as it suffers more attacks.
#### 3. Legal issues
Beyond sheer fines, regional, national, and international governments can also levy lawsuits against companies and individuals. Joe Sullivan, for example, a former CTO at Uber, was convicted of [covering up a 2016 data breach](https://www.washingtonpost.com/technology/2022/10/05/uber-obstruction-sullivan-hacking/) in 2022.
Even if individuals arent jailed and the company itself survives a lawsuit just fine, the consequences can still be meaningful. The previously cited Imprivata research shows that 40% of companies consider legal actions against a vendor to be another risk factor.
#### 4. Professional reputation
Parallel to the previously mentioned risk of more general reputation damage is the risk of damage to a companys professional reputation. Even if a data breach doesnt make headlines, employees, investors, and partners in your industry might still take heed.
The risk here gets worse when you consider the implications of a data breach. Many people, perhaps not entirely fairly, might doubt whether a company runs a good operation if it suffers repeated data breaches. Consider a representative [Glassdoor review of Uber](http://www.glassdoor.com/Reviews/Employee-Review-Uber-RVW39883443.htm):
![alt_text](./image3.png "image_tooltip")
Companies can also start negative feedback loops wherein repeated security issues give them a reputation as having a bad security team, meaning good security engineers might avoid working for the company to avoid association with that reputation.
#### 5. Contract cancellation
Fines arent the only form of monetary loss. Many companies build security risks into their vendor contracts, making it easy to sever the relationship or recoup their losses after a breach.
The previously cited Imprivata research shows that 59% of companies demand contracts that obligate vendors to “adhere to security and privacy practices.” The same proportion of companies 59% dont do security evaluations because they rely on the consequences of the security agreements in the contract.
### Whats old is new again: Why self-hosted software is making a comeback
Depending on your age and experience in the industry, the prospect of self-hosted software returning can range from plausible to laughable. The instinct to doubt makes sense SaaS became the dominant model of software delivery for a variety of valid reasons.
When the SaaS model emerged, it was clear that, in general, SaaS products were easier to use and often more effective than their self-hosted counterparts. SaaS products, for example, are:
- Easy to purchase often requiring little more than an account and a credit card.
- Easy to run, install, and upgrade.
- Easy to maintain especially given companies can rely not only on the resources of the SaaS vendor but on the distributed infrastructure of the cloud vendor the SaaS vendor is using.
That said, there are also compelling reasons to use self-hosted products. For example, with self-hosted products, companies can:
- Know where their data is located.
- Customize the application to their unique workflows.
- Shift financing from opex to capex, which often results in net cost savings.
- Trust in shared alignment. If you own and self-host a product, youre incentivized, in a way even the best SaaS vendor isnt, to keep it secure.
Authentication, what we specialize in here at Authentik, is a great example. The industry standard used to be a self-hosted products most commonly Microsoft ADFS but they were it was notoriously unwieldy, which gave companies like Auth0 and Okta a chance to take over the market.
The technology industry is cyclical, however, and our bet is that by applying lessons learned from SaaS, vendors can offer self-hosted products that are as good or better than SaaS products. Customers can then have their cake and eat it too.
#### Technology is cyclical, not regressive or progressive
At first glance, the idea of companies shifting back to a self-hosted model seems silly didnt we learn our lessons the first time? Its easy to assume that the technology industry progresses in a linear, upward fashion and infer that anything from the past is necessarily worse.
And while that might be true for specific products (floppy discs arent coming back, Im afraid to say) business and technology models can and have returned from the dead.
Marianne Bellotti, author of the book _Kill It with Fire: Manage Aging Computer Systems (and Future Proof Modern Ones)_, raises the example of thick and thin clients. Decades ago, most companies ran applications on bulky mainframes but before the founding of AWS in 2006, companies had shifted toward running applications on personal computers. But as the cloud grew, the mainframe model returned with companies “time-sharing,” in Bellottis words, on public clouds in much the same way they did on mainframes.
“Technology doesnt advance in a straight line,” argues Bellotti, “because a straight line is not actually efficient.” Instead, she writes, “Technology advances not by building on what came before, but by pivoting from it.” And there are significant, growing, multiplying reasons to not only reconsider self-hosting but reconsider private data centers and on-premises infrastructure.
#### Early signs of an unwinding trend
Its hard to believe given years of discourse and “thought leadership” about the cloud but there are signs of change. And to be clear, the claim here is not that AWS will suddenly collapse and IT admins will need to rapidly re-learn server racking skills; the claim is that there are reasons to reconsider self-hosting and evidence that more and more companies will do that reconsideration.
Consider the recent decisions of three established companies: 37signals, Dropbox, and Retool.
On Twitter, 37signals and Basecamp co-founder DHH [summarized the results](https://twitter.com/dhh/status/1613508201953038337?s=20&t=QFmwWhR0YSCygvItPwtC8w) of a recent accounting 37signals did across its properties. 37signals spent, in total, $3,201,564.24 on cloud in 2022 and in a [subsequent tweet](https://twitter.com/dhh/status/1613558939760689153?s=20&t=QFmwWhR0YSCygvItPwtC8w), DHH compared that cost to purchasing “insanely powerful iron” from Dell that included “288 vCPU, 15 TB NVM, 1.3TB RAM for $1,287/month over 3 years.”
![alt_text](./image1.png "image_tooltip")
In a [full post](https://dev.37signals.com/our-cloud-spend-in-2022/) on the 37signals blog, senior site reliability engineer Fernando Álvarez provided more details, writing that “In 2023, we hope to dramatically cut that bill by moving a lot of services and dependencies out of the cloud and onto our own hardware.”
Years prior to this planned shift, in 2015, Dropbox decided to “[reverse migrate](https://www.datacenterknowledge.com/manage/dropbox-s-reverse-migration-cloud-own-data-centers-five-years)” from the cloud (AWS, in this case) to privately owned data centers. Before the end of the year, Dropbox relocated 90% of its customer data to an in-house network of data centers. At the time, the shift broke headlines because it seemed so unique.
Five years on, as Scott Fulton writes, Dropboxs decision “is starting to look more like a pioneer expedition.” Dropbox is able to save money and manage their resources more closely. Fulton argues theres no reason this choice “should only fit Dropbox.” Given the ability to effectively plan capacity, Fulton writes that many companies could also “avoid the breaking point of cloud-based service affordability.”
This trend also emerges on a customer-facing level. In 2021, low code platform Retool announced a [self-hosted plan](https://twitter.com/retool/status/1404835350250344449?s=20&t=VMzl65BkICVb3v2HxIXsEw), enabling customers to host Retool inside their infrastructure. Self-hosting, again, is not new, nor is the presence of customers requesting a self-hosted option. The difference here is that Retool, a relatively new company, founded in 2017 and growing fast, found reason to prioritize building a self-hosted option. Retool even [says that](https://twitter.com/retool/status/1404835454948495360?s=20&t=VMzl65BkICVb3v2HxIXsEw) “Self-hosting has been a top request from our self-serve customers.”
Retool cited a couple of [key use cases](https://twitter.com/retool/status/1404835751833989125?s=20&t=VMzl65BkICVb3v2HxIXsEw), including companies working within a regulated industry and companies hosting sensitive data. Retool also made it clear, though, that self-hosting is typically burdensome and offering this plan required the company to modernize the deployment process and make deployment easy by integrating Docker and Kubernetes.
### SaaS should not be the default
David Bradbury, Oktas Chief Security Officer, concludes his [post](https://www.okta.com/blog/2022/03/oktas-investigation-of-the-january-2022-compromise/) explaining the companys investigation of the LAPSUS$ incident and their response to it in a familiar way: “As with all security incidents, there are many opportunities for us to improve our processes and our communications. Im confident that we are moving in the right direction and this incident will only serve to strengthen our commitment to security.”
You dont have to impugn Oktas commitment or accuse them of suppressing news about this breach to see the problem. SaaS companies, due to the very structure of their business and delivery models, cant be as aligned with your companys needs as you are. SaaS companies will always, at best, be “moving in the right direction,” whereas your company, if it self-hosts its software, wont have to worry about misaligned incentives.
There might be a paradigm shift in how the technology industry hosts its workloads and delivers its software. There might not be. Either way, more companies are realizing that SaaS isnt, and shouldnt be, the default.

View File

@ -34,6 +34,8 @@ Any additional `.yaml` file in `/blueprints` will be discovered and automaticall
To disable existing blueprints, an empty file can be mounted over the existing blueprint.
File-based blueprints are automatically removed once they become unavailable, however none of the objects created by those blueprints afre affected by this.
## Storage - OCI
Blueprints can also be stored in remote [OCI](https://opencontainers.org/) compliant registries. This includes GitHub Container Registry, Docker hub and many other registries.

View File

@ -60,3 +60,7 @@ Used by authentik's packaged blueprints to keep globals up-to-date. Should only
#### `blueprints.goauthentik.io/instantiate`:
Configure if this blueprint should automatically be instantiated (defaults to `"true"`). When set to `"false"`, blueprints are listed and available to be instantiated via API/Browser.
#### `blueprints.goauthentik.io/description`:
Optionally set a description, which can be seen in the web interface.

View File

@ -13,6 +13,7 @@
"@docusaurus/preset-classic": "2.2.0",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.2.1",
"disqus-react": "^1.1.5",
"postcss": "^8.4.21",
"rapidoc": "^9.3.4",
"react": "^17.0.2",
@ -5485,6 +5486,15 @@
"node": ">=8"
}
},
"node_modules/disqus-react": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/disqus-react/-/disqus-react-1.1.5.tgz",
"integrity": "sha512-9fdG5m6c3wJzlCDLaMheuUagMVj3s5qgUSXdekpCsvzYOKG21AiuOoqyDzA0oXrpPnYzgpnsvPYqZ+i0hJPGZw==",
"peerDependencies": {
"react": "^15.6.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.6.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/dns-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
@ -11512,9 +11522,9 @@
}
},
"node_modules/ua-parser-js": {
"version": "0.7.32",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==",
"version": "0.7.33",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==",
"funding": [
{
"type": "opencollective",
@ -16549,6 +16559,12 @@
"path-type": "^4.0.0"
}
},
"disqus-react": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/disqus-react/-/disqus-react-1.1.5.tgz",
"integrity": "sha512-9fdG5m6c3wJzlCDLaMheuUagMVj3s5qgUSXdekpCsvzYOKG21AiuOoqyDzA0oXrpPnYzgpnsvPYqZ+i0hJPGZw==",
"requires": {}
},
"dns-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
@ -20873,9 +20889,9 @@
"peer": true
},
"ua-parser-js": {
"version": "0.7.32",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw=="
"version": "0.7.33",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw=="
},
"unherit": {
"version": "1.1.3",

View File

@ -19,6 +19,7 @@
"@docusaurus/preset-classic": "2.2.0",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.2.1",
"disqus-react": "^1.1.5",
"postcss": "^8.4.21",
"rapidoc": "^9.3.4",
"react": "^17.0.2",

View File

@ -187,8 +187,8 @@ function Comparison() {
<td className="result passed">
<Check></Check>
</td>
<td className="result failed">
<X></X>
<td className="result passed">
<Check></Check>
</td>
</tr>
<tr>
@ -345,8 +345,8 @@ function Comparison() {
<td className="result passed">
<Check></Check>
</td>
<td className="result failed">
<X></X>
<td className="result passed">
<Check></Check>
</td>
</tr>
</tbody>

View File

@ -0,0 +1,76 @@
import React from "react";
import clsx from "clsx";
import {
HtmlClassNameProvider,
ThemeClassNames,
} from "@docusaurus/theme-common";
import {
BlogPostProvider,
useBlogPost,
} from "@docusaurus/theme-common/internal";
import BlogLayout from "@theme/BlogLayout";
import BlogPostItem from "@theme/BlogPostItem";
import BlogPostPaginator from "@theme/BlogPostPaginator";
import BlogPostPageMetadata from "@theme/BlogPostPage/Metadata";
import TOC from "@theme/TOC";
import { DiscussionEmbed } from "disqus-react";
function BlogPostPageContent({ sidebar, children }) {
const { metadata, toc } = useBlogPost();
const { nextItem, prevItem, frontMatter } = metadata;
const {
hide_table_of_contents: hideTableOfContents,
toc_min_heading_level: tocMinHeadingLevel,
toc_max_heading_level: tocMaxHeadingLevel,
} = frontMatter;
return (
<BlogLayout
sidebar={sidebar}
toc={
!hideTableOfContents && toc.length > 0 ? (
<TOC
toc={toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
/>
) : undefined
}
>
<BlogPostItem>{children}</BlogPostItem>
{(nextItem || prevItem) && (
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
)}
</BlogLayout>
);
}
export default function BlogPostPage(props) {
const BlogPostContent = props.content;
const title = props.content.frontMatter.title.substring(0, 200);
const fmtId = title.replace(/^\//, "").replaceAll(/[\s\/]/gi, "-");
const disqusId = fmtId == "" ? "main" : fmtId;
return (
<BlogPostProvider content={props.content} isBlogPostPage>
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogPostPage
)}
>
<BlogPostPageMetadata />
<BlogPostPageContent sidebar={props.sidebar}>
<BlogPostContent />
<DiscussionEmbed
shortname="goauthentik-io"
config={{
url: "https://goauthentik.io" + props.route.path,
identifier: disqusId,
title: title,
}}
/>
</BlogPostPageContent>
</HtmlClassNameProvider>
</BlogPostProvider>
);
}