Compare commits

..

3 Commits

Author SHA1 Message Date
b0e543a498 release: 2023.1.3 2023-03-02 20:18:04 +01:00
2fc3db2365 security: fix CVE-2023-26481 (#4832)
fix CVE-2023-26481

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-03-02 20:16:16 +01:00
ecf9c8fcdd website: always show build version in version dropdown
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

#3940
2023-02-16 14:39:28 +01:00
101 changed files with 822 additions and 1602 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2023.1.2
current_version = 2023.1.3
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)

View File

@ -2,7 +2,7 @@
from os import environ
from typing import Optional
__version__ = "2023.1.2"
__version__ = "2023.1.3"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -31,16 +31,6 @@ 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,12 +3,13 @@ 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_admin_user, create_test_flow
from authentik.core.tests.utils import 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
@ -35,18 +36,9 @@ class TestAPIAuth(TestCase):
def test_bearer_valid(self):
"""Test valid token"""
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=create_test_admin_user())
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=get_anonymous_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):
@ -64,7 +56,7 @@ class TestAPIAuth(TestCase):
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
)
refresh = RefreshToken.objects.create(
user=create_test_admin_user(),
user=get_anonymous_user(),
provider=provider,
refresh_token=generate_id(),
_scope=SCOPE_AUTHENTIK_API,
@ -77,7 +69,7 @@ class TestAPIAuth(TestCase):
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
)
refresh = RefreshToken.objects.create(
user=create_test_admin_user(),
user=get_anonymous_user(),
provider=provider,
refresh_token=generate_id(),
_scope="",

View File

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

View File

@ -9,9 +9,4 @@ 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,7 +4,6 @@ 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/default-tenant.yaml")
@apply_blueprint("default/90-default-tenant.yaml")
def test_decorator_static(self):
"""Test @apply_blueprint decorator"""
self.assertTrue(Tenant.objects.filter(domain="authentik-default").exists())

View File

@ -262,21 +262,15 @@ class TestBlueprintsV1(TransactionTestCase):
with transaction_rollback():
# First stage fields
username_prompt = Prompt.objects.create(
name=generate_id(),
field_key="username",
label="Username",
order=0,
type=FieldTypes.TEXT,
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,4 +3,3 @@
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,14 +219,3 @@ 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,8 +4,6 @@ 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
@ -59,7 +57,6 @@ from authentik.core.models import (
USER_ATTRIBUTE_SA,
USER_ATTRIBUTE_TOKEN_EXPIRING,
USER_PATH_SERVICE_ACCOUNT,
AuthenticatedSession,
Group,
Token,
TokenIntents,
@ -564,14 +561,3 @@ 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,12 +1,10 @@
"""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 AuthenticatedSession, User
from authentik.core.models import 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
@ -259,26 +257,3 @@ 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

@ -162,7 +162,7 @@ class FlowExecutorView(APIView):
token.delete()
if not isinstance(plan, FlowPlan):
return None
plan.context[PLAN_CONTEXT_IS_RESTORED] = True
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
self._logger.debug("f(exec): restored flow plan from token", plan=plan)
return plan

View File

@ -5,20 +5,13 @@ 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, Optional
from typing import Any
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
@ -45,47 +38,9 @@ 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:
@ -126,11 +81,11 @@ class ConfigLoader:
root[key] = self.update(root.get(key, {}), value)
else:
if isinstance(value, str):
value = self.parse_uri(value, root, key)
value = self.parse_uri(value)
root[key] = value
return root
def parse_uri(self, value: str, container: dict[str, Any], key: Optional[str] = None, ) -> str:
def parse_uri(self, value: str) -> str:
"""Parse string values which start with a URI"""
url = urlparse(value)
if url.scheme == "env":
@ -138,23 +93,13 @@ class ConfigLoader:
if url.scheme == "file":
try:
with open(url.path, "r", encoding="utf8") as _file:
value = _file.read()
if key:
self.observer.schedule(
ConfigLoader.FSObserver(
self,
url.path,
container,
key,
),
Path(url.path).parent,
)
value = _file.read().strip()
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, watch=True):
def update_from_file(self, path: str):
"""Update config from file contents"""
try:
with open(path, encoding="utf8") as file:
@ -162,8 +107,6 @@ 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:
@ -238,12 +181,13 @@ class ConfigLoader:
if comp not in root:
root[comp] = {}
root = root.get(comp, {})
self.parse_uri(value, root, path_parts[-1])
root[path_parts[-1]] = value
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 CONFIG, ENV_PREFIX, ConfigLoader
from authentik.lib.config import 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,13 +59,3 @@ 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"ghcr.io/goauthentik/{self.outpost.type}:latest"
image = f"goauthentik.io/{self.outpost.type}:latest"
self.client.images.pull(image)
return image

View File

@ -4,7 +4,6 @@ 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
@ -17,7 +16,6 @@ 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.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
user = self.get_pending_user()
if not user:
if "webauthn" not in response.data:
return self.executor.stage_invalid()

View File

@ -9,10 +9,9 @@ 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 SESSION_KEY_PLAN, FlowExecutorView
from authentik.flows.views.executor import FlowExecutorView
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.stages.authenticator_validate.challenge import (
@ -21,14 +20,10 @@ 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 (
SESSION_KEY_DEVICE_CHALLENGES,
AuthenticatorValidateStageView,
)
from authentik.stages.authenticator_validate.stage import 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):
@ -190,7 +185,10 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
def test_validate_challenge(self):
"""Test webauthn"""
device = WebAuthnDevice.objects.create(
request = get_request("/")
request.user = self.user
WebAuthnDevice.objects.create(
user=self.user,
public_key=(
"pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J"
@ -206,134 +204,49 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
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()))
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(
stage_view = AuthenticatorValidateStageView(
FlowExecutorView(flow=flow, current_stage=stage), request=request
)
request = get_request("/")
request.session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes(
(
"g98I51mQvZXo5lxLfhrD2zfolhZbLRyCgqkkYap1"
"jwSaJ13BguoJWCF9_Lg3AgO4Wh-Bqa556JE20oKsYbl6RA"
)
)
session.save()
request.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",
stage_view = AuthenticatorValidateStageView(
FlowExecutorView(flow=flow, current_stage=stage), request=request
)
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] = [
request.META["SERVER_NAME"] = "localhost"
request.META["SERVER_PORT"] = "9000"
validate_challenge_webauthn(
{
"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,
},
"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,
},
},
SERVER_NAME="localhost",
SERVER_PORT="9000",
stage_view,
self.user,
)
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

@ -15,7 +15,7 @@ from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTyp
from authentik.flows.models import FlowToken
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import ChallengeStageView
from authentik.flows.views.executor import QS_KEY_TOKEN, SESSION_KEY_GET
from authentik.flows.views.executor import QS_KEY_TOKEN
from authentik.stages.email.models import EmailStage
from authentik.stages.email.tasks import send_mails
from authentik.stages.email.utils import TemplateEmailMessage
@ -103,12 +103,14 @@ class EmailStageView(ChallengeStageView):
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
# Check if the user came back from the email link to verify
if QS_KEY_TOKEN in request.session.get(
SESSION_KEY_GET, {}
) and self.executor.plan.context.get(PLAN_CONTEXT_IS_RESTORED, False):
restore_token: FlowToken = self.executor.plan.context.get(PLAN_CONTEXT_IS_RESTORED, None)
user = self.get_pending_user()
if restore_token:
if restore_token.user != user:
self.logger.warning("Flow token for non-matching user, denying request")
return self.executor.stage_invalid()
messages.success(request, _("Successfully verified Email."))
if self.executor.current_stage.activate_user_on_success:
user = self.get_pending_user()
user.is_active = True
user.save()
return self.executor.stage_ok()

View File

@ -7,10 +7,9 @@ from django.core.mail.backends.smtp import EmailBackend as SMTPEmailBackend
from django.urls import reverse
from django.utils.http import urlencode
from authentik.core.models import Token
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.markers import StageMarker
from authentik.flows.models import FlowDesignation, FlowStageBinding
from authentik.flows.models import FlowDesignation, FlowStageBinding, FlowToken
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import SESSION_KEY_PLAN
@ -134,7 +133,7 @@ class TestEmailStage(FlowTestCase):
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
token: Token = Token.objects.get(user=self.user)
token: FlowToken = FlowToken.objects.get(user=self.user)
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
# Call the executor shell to preseed the session
@ -165,3 +164,43 @@ class TestEmailStage(FlowTestCase):
plan: FlowPlan = session[SESSION_KEY_PLAN]
self.assertEqual(plan.context[PLAN_CONTEXT_PENDING_USER], self.user)
self.assertTrue(plan.context[PLAN_CONTEXT_PENDING_USER].is_active)
def test_token_invalid_user(self):
"""Test with token with invalid user"""
# Make sure token exists
self.test_pending_user()
self.user.is_active = False
self.user.save()
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
# Set flow token user to a different user
token: FlowToken = FlowToken.objects.get(user=self.user)
token.user = create_test_admin_user()
token.save()
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
# Call the executor shell to preseed the session
url = reverse(
"authentik_api:flow-executor",
kwargs={"flow_slug": self.flow.slug},
)
url_query = urlencode(
{
QS_KEY_TOKEN: token.key,
}
)
url += f"?query={url_query}"
self.client.get(url)
# Call the actual executor to get the JSON Response
response = self.client.get(
reverse(
"authentik_api:flow-executor",
kwargs={"flow_slug": self.flow.slug},
)
)
self.assertEqual(response.status_code, 200)
self.assertStageResponse(response, component="ak-stage-access-denied")

View File

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

View File

@ -1,40 +0,0 @@
# 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,7 +96,6 @@ 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,7 +30,6 @@ 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,
@ -38,7 +37,6 @@ class TestPromptStage(FlowTestCase):
placeholder="USERNAME_PLACEHOLDER",
)
text_prompt = Prompt.objects.create(
name=generate_id(),
field_key="text_prompt",
label="TEXT_LABEL",
type=FieldTypes.TEXT,
@ -46,7 +44,6 @@ class TestPromptStage(FlowTestCase):
placeholder="TEXT_PLACEHOLDER",
)
email_prompt = Prompt.objects.create(
name=generate_id(),
field_key="email_prompt",
label="EMAIL_LABEL",
type=FieldTypes.EMAIL,
@ -54,7 +51,6 @@ class TestPromptStage(FlowTestCase):
placeholder="EMAIL_PLACEHOLDER",
)
password_prompt = Prompt.objects.create(
name=generate_id(),
field_key="password_prompt",
label="PASSWORD_LABEL",
type=FieldTypes.PASSWORD,
@ -62,7 +58,6 @@ class TestPromptStage(FlowTestCase):
placeholder="PASSWORD_PLACEHOLDER",
)
password2_prompt = Prompt.objects.create(
name=generate_id(),
field_key="password2_prompt",
label="PASSWORD_LABEL",
type=FieldTypes.PASSWORD,
@ -70,7 +65,6 @@ class TestPromptStage(FlowTestCase):
placeholder="PASSWORD_PLACEHOLDER",
)
number_prompt = Prompt.objects.create(
name=generate_id(),
field_key="number_prompt",
label="NUMBER_LABEL",
type=FieldTypes.NUMBER,
@ -78,14 +72,12 @@ 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,10 +17,9 @@ 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:
@ -29,10 +28,9 @@ 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

@ -17,10 +17,9 @@ 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,10 +21,9 @@ 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:
@ -37,10 +36,9 @@ 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:
@ -53,10 +51,9 @@ 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:
@ -69,10 +66,9 @@ 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,11 +19,10 @@ entries:
required: true
sub_text: ''
type: static
field_key: oobe-header-text
label: oobe-header-text
id: prompt-field-header
identifiers:
name: initial-setup-field-header
field_key: oobe-header-text
label: oobe-header-text
model: authentik_stages_prompt.prompt
- attrs:
order: 101
@ -32,11 +31,10 @@ entries:
required: true
sub_text: ''
type: email
field_key: email
label: Email
id: prompt-field-email
identifiers:
name: initial-setup-field-email
field_key: admin_email
label: Email
model: authentik_stages_prompt.prompt
- attrs:
order: 300
@ -45,11 +43,10 @@ entries:
required: true
sub_text: ''
type: password
field_key: password
label: Password
id: prompt-field-password
identifiers:
name: initial-setup-field-password
field_key: password
label: Password
model: authentik_stages_prompt.prompt
- attrs:
order: 301
@ -58,11 +55,10 @@ entries:
required: true
sub_text: ''
type: password
field_key: password_repeat
label: Password (repeat)
id: prompt-field-password-repeat
identifiers:
name: initial-setup-field-password-repeat
field_key: password_repeat
label: Password (repeat)
model: authentik_stages_prompt.prompt
- attrs:
expression: |
@ -70,6 +66,8 @@ 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

@ -13,61 +13,56 @@ entries:
title: Welcome to authentik!
designation: enrollment
authentication: require_unauthenticated
- id: prompt-field-username
model: authentik_stages_prompt.prompt
identifiers:
name: default-enrollment-field-username
attrs:
- identifiers:
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:
name: default-enrollment-field-password
field_key: password
label: 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:
name: default-enrollment-field-password-repeat
field_key: password_repeat
label: 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:
name: default-enrollment-field-name
field_key: name
label: 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:
name: default-enrollment-field-email
field_key: email
label: 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,60 +14,55 @@ entries:
designation: enrollment
authentication: require_unauthenticated
- identifiers:
name: default-enrollment-field-username
field_key: username
label: 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:
name: default-enrollment-field-password
field_key: password
label: 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:
name: default-enrollment-field-password-repeat
field_key: password_repeat
label: 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:
name: default-enrollment-field-name
field_key: name
label: 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:
name: default-enrollment-field-email
field_key: email
label: 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,24 +14,22 @@ entries:
designation: recovery
authentication: require_unauthenticated
- identifiers:
name: default-recovery-field-password
field_key: password
label: 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:
name: default-recovery-field-password-repeat
field_key: password_repeat
label: 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)
@ -154,6 +152,7 @@ entries:
policy: !KeyOf default-recovery-skip-if-restored
target: !KeyOf flow-binding-email
order: 0
state: absent
model: authentik_policies.policybinding
attrs:
negate: false

View File

@ -1,70 +0,0 @@
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

View File

@ -32,7 +32,7 @@ services:
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.1.2}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.1.3}
restart: unless-stopped
command: server
environment:
@ -50,7 +50,7 @@ services:
- "${AUTHENTIK_PORT_HTTP:-9000}:9000"
- "${AUTHENTIK_PORT_HTTPS:-9443}:9443"
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.1.2}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.1.3}
restart: unless-stopped
command: worker
environment:

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.2023012.2
goauthentik.io/api/v3 v3.2023010.1
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.2023012.2 h1:aP/JlZCxGaPuV+vPabL3Niz7lWAYnNolrINYSFBDFG0=
goauthentik.io/api/v3 v3.2023012.2/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
goauthentik.io/api/v3 v3.2023010.1 h1:DQuf201pD+OKVnzpLKCaKw8IS397ewzMzFIGXyhmQ3w=
goauthentik.io/api/v3 v3.2023010.1/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion())
}
const VERSION = "2023.1.2"
const VERSION = "2023.1.3"

View File

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

View File

@ -1,16 +1,15 @@
package ak
import (
"context"
"time"
)
func (a *APIController) startPeriodicalTasks() {
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)
}
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()
}
}()
}

View File

@ -143,10 +143,6 @@ 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,14 +1,9 @@
package bind
import (
"context"
"github.com/nmcclain/ldap"
)
import "github.com/nmcclain/ldap"
type Binder interface {
GetUsername(string) (string, error)
Bind(username string, req *Request) (ldap.LDAPResultCode, error)
Unbind(username string, req *Request) (ldap.LDAPResultCode, error)
TimerFlowCacheExpiry(context.Context)
TimerFlowCacheExpiry()
}

View File

@ -1,98 +0,0 @@
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,10 +5,16 @@ 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"
)
@ -47,6 +53,90 @@ 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 {
@ -63,8 +153,8 @@ func (db *DirectBinder) SearchAccessCheck(user api.UserSelf) *string {
return nil
}
func (db *DirectBinder) TimerFlowCacheExpiry(ctx context.Context) {
fe := flow.NewFlowExecutor(ctx, db.si.GetAuthenticationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{})
func (db *DirectBinder) TimerFlowCacheExpiry() {
fe := flow.NewFlowExecutor(context.Background(), db.si.GetFlowSlug(), 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

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

View File

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

View File

@ -28,16 +28,6 @@ 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 {
@ -47,7 +37,6 @@ 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))
@ -64,23 +53,22 @@ func (ls *LDAPServer) Refresh() error {
}
providers[idx] = &ProviderInstance{
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,
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,
}
if kp := provider.Certificate.Get(); kp != nil {
err := ls.cs.AddKeypair(*kp)

View File

@ -11,8 +11,7 @@ type LDAPServerInstance interface {
GetAPIClient() *api.APIClient
GetOutpostName() string
GetAuthenticationFlowSlug() string
GetInvalidationFlowSlug() string
GetFlowSlug() string
GetAppSlug() string
GetSearchAllowedGroups() []*strfmt.UUID
@ -33,7 +32,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

@ -1,53 +0,0 @@
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(context.Context) {}
func (ps *ProxyServer) TimerFlowCacheExpiry() {}
func (ps *ProxyServer) GetCertificate(serverName string) *tls.Certificate {
app, ok := ps.apps[serverName]
@ -163,10 +163,6 @@ 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.1.0"
version = "7.0.5"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{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"},
{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"},
]
[package.extras]
@ -2851,14 +2851,14 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "selenium"
version = "4.8.0"
version = "4.7.2"
description = ""
category = "dev"
optional = false
python-versions = ">=3.7"
python-versions = "~=3.7"
files = [
{file = "selenium-4.8.0-py3-none-any.whl", hash = "sha256:20f28ee4ea9b273b4112a7df5276ebb3052f79ff6eff42a564db6143e5926683"},
{file = "selenium-4.8.0.tar.gz", hash = "sha256:fee36724d6cf0b18c73781bb8ec7be4a35ab1e2564e64e64e64da75e50e052af"},
{file = "selenium-4.7.2-py3-none-any.whl", hash = "sha256:06a1c7d9f313130b21c3218ddd8852070d0e7419afdd31f96160cd576555a5ce"},
{file = "selenium-4.7.2.tar.gz", hash = "sha256:3aefa14a28a42e520550c1cd0f29cf1d566328186ea63aa9a3e01fb265b5894d"},
]
[package.dependencies]
@ -2869,14 +2869,14 @@ urllib3 = {version = ">=1.26,<2.0", extras = ["socks"]}
[[package]]
name = "sentry-sdk"
version = "1.14.0"
version = "1.13.0"
description = "Python client for Sentry (https://sentry.io)"
category = "main"
optional = false
python-versions = "*"
files = [
{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"},
{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"},
]
[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.35b0)"]
opentelemetry = ["opentelemetry-distro (>=0.350b0)"]
pure-eval = ["asttokens", "executing", "pure-eval"]
pymongo = ["pymongo (>=3.1)"]
pyspark = ["pyspark (>=2.4.4)"]

View File

@ -114,7 +114,7 @@ filterwarnings = [
[tool.poetry]
name = "authentik"
version = "2023.1.2"
version = "2023.1.3"
description = ""
authors = ["authentik Team <hello@goauthentik.io>"]

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2023.1.2
version: 2023.1.3
description: Making authentication simple.
contact:
email: hello@goauthentik.io
@ -23008,10 +23008,6 @@ paths:
name: label
schema:
type: string
- in: query
name: name
schema:
type: string
- name: ordering
required: false
in: query
@ -34267,9 +34263,6 @@ components:
type: object
description: Prompt Serializer
properties:
name:
type: string
minLength: 1
field_key:
type: string
minLength: 1
@ -35285,8 +35278,6 @@ 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
@ -35313,7 +35304,6 @@ components:
required:
- field_key
- label
- name
- pk
- type
PromptChallenge:
@ -35355,9 +35345,6 @@ components:
type: object
description: Prompt Serializer
properties:
name:
type: string
minLength: 1
field_key:
type: string
minLength: 1
@ -35386,7 +35373,6 @@ components:
required:
- field_key
- label
- name
- type
PromptStage:
type: object

View File

@ -26,8 +26,8 @@ class TestFlowsAuthenticator(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint("default/flow-default-authenticator-totp-setup.yaml")
@apply_blueprint("default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint("default/flow-default-authenticator-static-setup.yaml")
@apply_blueprint("default/20-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,28 +41,19 @@ class TestFlowsEnroll(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
def test_enroll_2_step(self):
"""Test 2-step enroll flow"""
# First stage fields
username_prompt = Prompt.objects.create(
name=generate_id(),
field_key="username",
label="Username",
order=0,
type=FieldTypes.TEXT,
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,
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,
@ -71,10 +62,10 @@ class TestFlowsEnroll(SeleniumTestCase):
# Second stage fields
name_field = Prompt.objects.create(
name=generate_id(), field_key="name", label="Name", order=0, type=FieldTypes.TEXT
field_key="name", label="Name", order=0, type=FieldTypes.TEXT
)
email = Prompt.objects.create(
name=generate_id(), field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
)
# Stages
@ -116,28 +107,19 @@ class TestFlowsEnroll(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
def test_enroll_email(self):
"""Test enroll with Email verification"""
# First stage fields
username_prompt = Prompt.objects.create(
name=generate_id(),
field_key="username",
label="Username",
order=0,
type=FieldTypes.TEXT,
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,
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,
@ -146,10 +128,10 @@ class TestFlowsEnroll(SeleniumTestCase):
# Second stage fields
name_field = Prompt.objects.create(
name=generate_id(), field_key="name", label="Name", order=0, type=FieldTypes.TEXT
field_key="name", label="Name", order=0, type=FieldTypes.TEXT
)
email = Prompt.objects.create(
name=generate_id(), field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-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/flow-password-change.yaml")
@apply_blueprint("default/0-flow-password-change.yaml")
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -114,12 +114,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -189,12 +189,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -116,12 +116,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -178,12 +178,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -249,12 +249,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -329,12 +329,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -123,12 +123,12 @@ class TestProviderProxy(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
@ -200,12 +200,12 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -133,12 +133,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -217,12 +217,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -301,12 +301,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -376,12 +376,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"system/providers-saml.yaml",
@ -425,12 +425,12 @@ class TestProviderSAML(SeleniumTestCase):
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
)
@apply_blueprint(
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-explicit-consent.yaml",
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-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/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
"default/10-flow-default-authentication-flow.yaml",
"default/10-flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-source-authentication.yaml",
"default/flow-default-source-enrollment.yaml",
"default/flow-default-source-pre-authentication.yaml",
"default/20-flow-default-source-authentication.yaml",
"default/20-flow-default-source-enrollment.yaml",
"default/20-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.2-1674559422",
"@goauthentik/api": "^2023.1.0-1674058489",
"@hcaptcha/types": "^1.0.3",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.16.1",
"@lingui/cli": "^3.16.0",
"@lingui/core": "^3.16.0",
"@lingui/detect-locale": "^3.16.1",
"@lingui/macro": "^3.16.1",
"@lingui/detect-locale": "^3.16.0",
"@lingui/macro": "^3.16.0",
"@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.33.0",
"@sentry/tracing": "^7.33.0",
"@sentry/browser": "^7.31.1",
"@sentry/tracing": "^7.31.1",
"@squoosh/cli": "^0.7.3",
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
"@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.7",
"@types/codemirror": "5.60.6",
"@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"@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.291",
"pyright": "^1.1.290",
"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.2-1674559422",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.2-1674559422.tgz",
"integrity": "sha512-zkbsIGy9J5QvtL70JH/svY0vGvbJhfYkhBxlt61dxCI3qDaof2RjJvm913OP6N/h8JoSzbyj8uZGJs4y8pHLhw=="
"version": "2023.1.0-1674058489",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.0-1674058489.tgz",
"integrity": "sha512-k100Z1Tx4o7EsIq+tezavHSSddpr51NlGJZyR9QHsaQq6K+BpxZVcozR7xau7zK615JAwljWeG4yfEq9to++rw=="
},
"node_modules/@hcaptcha/types": {
"version": "1.0.3",
@ -2215,13 +2215,13 @@
}
},
"node_modules/@lingui/babel-plugin-extract-messages": {
"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==",
"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==",
"dependencies": {
"@babel/generator": "^7.11.6",
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.1",
"@lingui/conf": "3.16.0",
"mkdirp": "^1.0.4"
},
"engines": {
@ -2229,18 +2229,18 @@
}
},
"node_modules/@lingui/cli": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.1.tgz",
"integrity": "sha512-20IezHftBqJe8EYls+fwftni5f2dBWFe+d2J7a6DRKuvuSxBD2UforLPna9dq1Ya84MnZGQBhhY4zSXIXvO+fA==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.0.tgz",
"integrity": "sha512-6sUVpA6UB4BwNtLjC9aG62QvxkhHwVmDOi9vO8kb9W4SKPBaMdHuaxEsVVBuD1ZWcFZULcNUmPh9kKAnMRkHHw==",
"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.1",
"@lingui/conf": "3.16.1",
"@lingui/core": "3.16.1",
"@lingui/babel-plugin-extract-messages": "3.16.0",
"@lingui/conf": "3.16.0",
"@lingui/core": "3.16.0",
"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.1",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.1.tgz",
"integrity": "sha512-F8aYpjFXItmJLm09BxDzUZTsK0bB7nxLpMkUmiawVuuyB+66mBanOQ1nQEn8+SdJHCqJo9sI1ZSg+1uFequGzg==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.0.tgz",
"integrity": "sha512-65X3TySGzeYjVNE3YZDpF6LTYxpUiSRSyTJjEGB7ZSPpuL6VDMEUGCDsnur2ix8D+I8w0ife6ks4HPQt6owZmw==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"chalk": "^4.1.0",
@ -2422,9 +2422,9 @@
}
},
"node_modules/@lingui/core": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.1.tgz",
"integrity": "sha512-F/oos3IquuA71eVUfVBFY4teq9QuE3enGV65AMb9VB0vUTxsy6HAOIo1A2aZ4wJ0ST8V7FhYshYRU6qXguRjMQ==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.0.tgz",
"integrity": "sha512-2uZvxHv4IWF7xIRG1o4oXDFCrAhE0945Ses1eALmv/NqQ8BslXWWSq0Zf51qt+ZqQ3RfzCdl4kslZEqdGRe0gw==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@messageformat/parser": "^5.0.0",
@ -2435,20 +2435,20 @@
}
},
"node_modules/@lingui/detect-locale": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.1.tgz",
"integrity": "sha512-zDm9grmwPjQ5FXoIpkt0Fvq2bH+d01lI7zTmNFdTcM4IK4vsgAYqSY2h94W+7A3vyHEeaIULm5XtLBePSLJLcQ==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.0.tgz",
"integrity": "sha512-7FQy1ccPnVe0/GNAzTmEDrsPHZV1/nSsK6fRKyuOFUkiItc1bVDmlpVfM4UXerERx6Nez+N99Z6Vjc6rg3LARw==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@lingui/macro": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.1.tgz",
"integrity": "sha512-CmR6u37Wzb+P4FGJJtbjY3SSX6vQb1IOj3Wcv9DYKvi1nepMvtvc5hcgCQKmqTdDi29fx6re5lmfG8YnthLkwA==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.0.tgz",
"integrity": "sha512-3HfP1Bqr4i60P3LoQq/ukhDvel4a5oBSMPRuYBUpwqdnKOAbZuN5vnZmu3TrlXntWTYOvrwa71D8SYGmvevelw==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.1",
"@lingui/conf": "3.16.0",
"ramda": "^0.27.1"
},
"engines": {
@ -2974,14 +2974,14 @@
}
},
"node_modules/@sentry/browser": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.33.0.tgz",
"integrity": "sha512-bvExBTyLb7cLWLkHh0gch2W/oSw08Yo8DgEc+KkikOnvWd/xoEWUsYNydYGzV+bL1jqcOErsZy0fVsbzTmh71g==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.31.1.tgz",
"integrity": "sha512-Rg9F61S1tz1Dv3iUyyGP26bxoi7WJAG2+f2fBbSmFuJ+JTH4Jvu2/F1bBig8Dz01ejzVhbNSUUCfoDhSvksIsQ==",
"dependencies": {
"@sentry/core": "7.33.0",
"@sentry/replay": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"@sentry/core": "7.31.1",
"@sentry/replay": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"tslib": "^1.9.3"
},
"engines": {
@ -2994,12 +2994,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/core": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.33.0.tgz",
"integrity": "sha512-mrSgUnXjxHVi0cVea1lv7gC/Y66ya2a3atCHaPEij/+l+3APg5d0Ixt1zMx5YllMiZKf6wpxlZ0uwXcqdAAw+w==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.31.1.tgz",
"integrity": "sha512-quaNU6z8jabmatBTDi28Wpff2yzfWIp/IU4bbi2QOtEiCNT+TQJXqlRTRMu9xLrX7YzyKCL5X2gbit/85lyWUg==",
"dependencies": {
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3012,26 +3012,26 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/replay": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.33.0.tgz",
"integrity": "sha512-m6xpSdjsNCCGxAkk5ikPFv/sQAfWtieMEXLdeDZE9jnroVozweHpsUhZYhqzTpxVC5SA3jPyTQ6Ods5gRvTBfA==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.31.1.tgz",
"integrity": "sha512-sLArvwZn6IwA/bASctyhxN7LhdCXJvMmyTynRfmk7pzuNzBMc5CNlHeIsDpHrfQuH53IKicvl6cHnHyclu5DSA==",
"dependencies": {
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0"
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@sentry/tracing": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.33.0.tgz",
"integrity": "sha512-MtcKyW/QJgXGrHf5+205xnIIl7yIT99MzuTkuKzQwmnmy/siD3U0X8RoCaGLzj6kkSIu4m7vyQZoyd3J+5D8lw==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.31.1.tgz",
"integrity": "sha512-kW6vNwddp2Ycq2JfTzveUEIRF9YQwvl7L6BBoOZt9oVnYlsPipEeyU2Q277LatHldr8hDo2tbz/vz2BQjO5GSw==",
"dependencies": {
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3044,19 +3044,19 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/types": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.33.0.tgz",
"integrity": "sha512-5kkmYjtBWSbPxfYGiXdZFPS6xpFBNlXvDqeX4NpCFXz6/LiEDn6tZ61kuCSFb8MZlyqyCX5WsP3aiI2FJfpGIA==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.31.1.tgz",
"integrity": "sha512-1uzr2l0AxEnxUX/S0EdmXUQ15/kDsam8Nbdw4Gai8SU764XwQgA/TTjoewVP597CDI/AHKan67Y630/Ylmkx9w==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/utils": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.33.0.tgz",
"integrity": "sha512-msp02GV1gOfaN5FjKjWxI00rtbYLXEE5cTGldhs/Dt9KI63dDk1nwPDkSLhg6joqRItAq0thlBh6un717HdWbg==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.31.1.tgz",
"integrity": "sha512-ZsIPq29aNdP9q3R7qIzJhZ9WW+4DzE9g5SfGwx3UjTIxoRRBfdUJUbf7S+LKEdvCkKbyoDt6FLt5MiSJV43xBA==",
"dependencies": {
"@sentry/types": "7.33.0",
"@sentry/types": "7.31.1",
"tslib": "^1.9.3"
},
"engines": {
@ -3256,9 +3256,9 @@
}
},
"node_modules/@types/codemirror": {
"version": "5.60.7",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz",
"integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==",
"version": "5.60.6",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz",
"integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==",
"dependencies": {
"@types/tern": "*"
}
@ -3404,13 +3404,13 @@
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"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==",
"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==",
"dependencies": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/type-utils": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/type-utils": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"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.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
"dependencies": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"debug": "^4.3.4"
},
"engines": {
@ -3476,12 +3476,12 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
"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==",
"dependencies": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0"
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -3492,12 +3492,12 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz",
"integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==",
"dependencies": {
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
@ -3518,9 +3518,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@ -3530,12 +3530,12 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"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==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
"dependencies": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -3570,15 +3570,15 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz",
"integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==",
"dependencies": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"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.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==",
"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==",
"dependencies": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/types": "5.48.2",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@ -8170,9 +8170,9 @@
}
},
"node_modules/pyright": {
"version": "1.1.291",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.291.tgz",
"integrity": "sha512-wkSlCEhF1OwtJqHuifwxk76UMr1NPqsAbPIlCh7tiZx8VUdZs17NCGNorhyb2M+GFEvp8pod9Xyu1LXN1JL2kQ==",
"version": "1.1.290",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.290.tgz",
"integrity": "sha512-iHT8G2+R/GUfYWxxd4ud5Lj/0H0bE1eWVR0avKKgPtkBeLkUz3sorjKpfuaJdsuJNrunzMtZsmYPD366t39klg==",
"bin": {
"pyright": "index.js",
"pyright-langserver": "langserver.index.js"
@ -11437,9 +11437,9 @@
"integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A=="
},
"@goauthentik/api": {
"version": "2023.1.2-1674559422",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.2-1674559422.tgz",
"integrity": "sha512-zkbsIGy9J5QvtL70JH/svY0vGvbJhfYkhBxlt61dxCI3qDaof2RjJvm913OP6N/h8JoSzbyj8uZGJs4y8pHLhw=="
"version": "2023.1.0-1674058489",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.0-1674058489.tgz",
"integrity": "sha512-k100Z1Tx4o7EsIq+tezavHSSddpr51NlGJZyR9QHsaQq6K+BpxZVcozR7xau7zK615JAwljWeG4yfEq9to++rw=="
},
"@hcaptcha/types": {
"version": "1.0.3",
@ -11650,29 +11650,29 @@
}
},
"@lingui/babel-plugin-extract-messages": {
"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==",
"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==",
"requires": {
"@babel/generator": "^7.11.6",
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.1",
"@lingui/conf": "3.16.0",
"mkdirp": "^1.0.4"
}
},
"@lingui/cli": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.1.tgz",
"integrity": "sha512-20IezHftBqJe8EYls+fwftni5f2dBWFe+d2J7a6DRKuvuSxBD2UforLPna9dq1Ya84MnZGQBhhY4zSXIXvO+fA==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.0.tgz",
"integrity": "sha512-6sUVpA6UB4BwNtLjC9aG62QvxkhHwVmDOi9vO8kb9W4SKPBaMdHuaxEsVVBuD1ZWcFZULcNUmPh9kKAnMRkHHw==",
"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.1",
"@lingui/conf": "3.16.1",
"@lingui/core": "3.16.1",
"@lingui/babel-plugin-extract-messages": "3.16.0",
"@lingui/conf": "3.16.0",
"@lingui/core": "3.16.0",
"babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7",
"chalk": "^4.1.0",
@ -11744,9 +11744,9 @@
}
},
"@lingui/conf": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.1.tgz",
"integrity": "sha512-F8aYpjFXItmJLm09BxDzUZTsK0bB7nxLpMkUmiawVuuyB+66mBanOQ1nQEn8+SdJHCqJo9sI1ZSg+1uFequGzg==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.0.tgz",
"integrity": "sha512-65X3TySGzeYjVNE3YZDpF6LTYxpUiSRSyTJjEGB7ZSPpuL6VDMEUGCDsnur2ix8D+I8w0ife6ks4HPQt6owZmw==",
"requires": {
"@babel/runtime": "^7.11.2",
"chalk": "^4.1.0",
@ -11802,9 +11802,9 @@
}
},
"@lingui/core": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.1.tgz",
"integrity": "sha512-F/oos3IquuA71eVUfVBFY4teq9QuE3enGV65AMb9VB0vUTxsy6HAOIo1A2aZ4wJ0ST8V7FhYshYRU6qXguRjMQ==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.0.tgz",
"integrity": "sha512-2uZvxHv4IWF7xIRG1o4oXDFCrAhE0945Ses1eALmv/NqQ8BslXWWSq0Zf51qt+ZqQ3RfzCdl4kslZEqdGRe0gw==",
"requires": {
"@babel/runtime": "^7.11.2",
"@messageformat/parser": "^5.0.0",
@ -11812,17 +11812,17 @@
}
},
"@lingui/detect-locale": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.1.tgz",
"integrity": "sha512-zDm9grmwPjQ5FXoIpkt0Fvq2bH+d01lI7zTmNFdTcM4IK4vsgAYqSY2h94W+7A3vyHEeaIULm5XtLBePSLJLcQ=="
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.0.tgz",
"integrity": "sha512-7FQy1ccPnVe0/GNAzTmEDrsPHZV1/nSsK6fRKyuOFUkiItc1bVDmlpVfM4UXerERx6Nez+N99Z6Vjc6rg3LARw=="
},
"@lingui/macro": {
"version": "3.16.1",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.1.tgz",
"integrity": "sha512-CmR6u37Wzb+P4FGJJtbjY3SSX6vQb1IOj3Wcv9DYKvi1nepMvtvc5hcgCQKmqTdDi29fx6re5lmfG8YnthLkwA==",
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.0.tgz",
"integrity": "sha512-3HfP1Bqr4i60P3LoQq/ukhDvel4a5oBSMPRuYBUpwqdnKOAbZuN5vnZmu3TrlXntWTYOvrwa71D8SYGmvevelw==",
"requires": {
"@babel/runtime": "^7.11.2",
"@lingui/conf": "3.16.1",
"@lingui/conf": "3.16.0",
"ramda": "^0.27.1"
}
},
@ -12194,14 +12194,14 @@
}
},
"@sentry/browser": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.33.0.tgz",
"integrity": "sha512-bvExBTyLb7cLWLkHh0gch2W/oSw08Yo8DgEc+KkikOnvWd/xoEWUsYNydYGzV+bL1jqcOErsZy0fVsbzTmh71g==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.31.1.tgz",
"integrity": "sha512-Rg9F61S1tz1Dv3iUyyGP26bxoi7WJAG2+f2fBbSmFuJ+JTH4Jvu2/F1bBig8Dz01ejzVhbNSUUCfoDhSvksIsQ==",
"requires": {
"@sentry/core": "7.33.0",
"@sentry/replay": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"@sentry/core": "7.31.1",
"@sentry/replay": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12213,12 +12213,12 @@
}
},
"@sentry/core": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.33.0.tgz",
"integrity": "sha512-mrSgUnXjxHVi0cVea1lv7gC/Y66ya2a3atCHaPEij/+l+3APg5d0Ixt1zMx5YllMiZKf6wpxlZ0uwXcqdAAw+w==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.31.1.tgz",
"integrity": "sha512-quaNU6z8jabmatBTDi28Wpff2yzfWIp/IU4bbi2QOtEiCNT+TQJXqlRTRMu9xLrX7YzyKCL5X2gbit/85lyWUg==",
"requires": {
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12230,23 +12230,23 @@
}
},
"@sentry/replay": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.33.0.tgz",
"integrity": "sha512-m6xpSdjsNCCGxAkk5ikPFv/sQAfWtieMEXLdeDZE9jnroVozweHpsUhZYhqzTpxVC5SA3jPyTQ6Ods5gRvTBfA==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.31.1.tgz",
"integrity": "sha512-sLArvwZn6IwA/bASctyhxN7LhdCXJvMmyTynRfmk7pzuNzBMc5CNlHeIsDpHrfQuH53IKicvl6cHnHyclu5DSA==",
"requires": {
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0"
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1"
}
},
"@sentry/tracing": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.33.0.tgz",
"integrity": "sha512-MtcKyW/QJgXGrHf5+205xnIIl7yIT99MzuTkuKzQwmnmy/siD3U0X8RoCaGLzj6kkSIu4m7vyQZoyd3J+5D8lw==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.31.1.tgz",
"integrity": "sha512-kW6vNwddp2Ycq2JfTzveUEIRF9YQwvl7L6BBoOZt9oVnYlsPipEeyU2Q277LatHldr8hDo2tbz/vz2BQjO5GSw==",
"requires": {
"@sentry/core": "7.33.0",
"@sentry/types": "7.33.0",
"@sentry/utils": "7.33.0",
"@sentry/core": "7.31.1",
"@sentry/types": "7.31.1",
"@sentry/utils": "7.31.1",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12258,16 +12258,16 @@
}
},
"@sentry/types": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.33.0.tgz",
"integrity": "sha512-5kkmYjtBWSbPxfYGiXdZFPS6xpFBNlXvDqeX4NpCFXz6/LiEDn6tZ61kuCSFb8MZlyqyCX5WsP3aiI2FJfpGIA=="
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.31.1.tgz",
"integrity": "sha512-1uzr2l0AxEnxUX/S0EdmXUQ15/kDsam8Nbdw4Gai8SU764XwQgA/TTjoewVP597CDI/AHKan67Y630/Ylmkx9w=="
},
"@sentry/utils": {
"version": "7.33.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.33.0.tgz",
"integrity": "sha512-msp02GV1gOfaN5FjKjWxI00rtbYLXEE5cTGldhs/Dt9KI63dDk1nwPDkSLhg6joqRItAq0thlBh6un717HdWbg==",
"version": "7.31.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.31.1.tgz",
"integrity": "sha512-ZsIPq29aNdP9q3R7qIzJhZ9WW+4DzE9g5SfGwx3UjTIxoRRBfdUJUbf7S+LKEdvCkKbyoDt6FLt5MiSJV43xBA==",
"requires": {
"@sentry/types": "7.33.0",
"@sentry/types": "7.31.1",
"tslib": "^1.9.3"
},
"dependencies": {
@ -12430,9 +12430,9 @@
}
},
"@types/codemirror": {
"version": "5.60.7",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz",
"integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==",
"version": "5.60.6",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz",
"integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==",
"requires": {
"@types/tern": "*"
}
@ -12577,13 +12577,13 @@
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
"@typescript-eslint/eslint-plugin": {
"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==",
"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==",
"requires": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/type-utils": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/type-utils": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"debug": "^4.3.4",
"ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0",
@ -12603,48 +12603,48 @@
}
},
"@typescript-eslint/parser": {
"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==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
"requires": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
"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==",
"requires": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0"
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2"
}
},
"@typescript-eslint/type-utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz",
"integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==",
"requires": {
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@typescript-eslint/typescript-estree": "5.48.2",
"@typescript-eslint/utils": "5.48.2",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/types": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg=="
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA=="
},
"@typescript-eslint/typescript-estree": {
"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==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
"requires": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/visitor-keys": "5.48.2",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -12663,15 +12663,15 @@
}
},
"@typescript-eslint/utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
"version": "5.48.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz",
"integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==",
"requires": {
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.48.2",
"@typescript-eslint/types": "5.48.2",
"@typescript-eslint/typescript-estree": "5.48.2",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
@ -12688,11 +12688,11 @@
}
},
"@typescript-eslint/visitor-keys": {
"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==",
"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==",
"requires": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/types": "5.48.2",
"eslint-visitor-keys": "^3.3.0"
},
"dependencies": {
@ -16055,9 +16055,9 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"pyright": {
"version": "1.1.291",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.291.tgz",
"integrity": "sha512-wkSlCEhF1OwtJqHuifwxk76UMr1NPqsAbPIlCh7tiZx8VUdZs17NCGNorhyb2M+GFEvp8pod9Xyu1LXN1JL2kQ=="
"version": "1.1.290",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.290.tgz",
"integrity": "sha512-iHT8G2+R/GUfYWxxd4ud5Lj/0H0bE1eWVR0avKKgPtkBeLkUz3sorjKpfuaJdsuJNrunzMtZsmYPD366t39klg=="
},
"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.2-1674559422",
"@goauthentik/api": "^2023.1.0-1674058489",
"@hcaptcha/types": "^1.0.3",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.16.1",
"@lingui/cli": "^3.16.0",
"@lingui/core": "^3.16.0",
"@lingui/detect-locale": "^3.16.1",
"@lingui/macro": "^3.16.1",
"@lingui/detect-locale": "^3.16.0",
"@lingui/macro": "^3.16.0",
"@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.33.0",
"@sentry/tracing": "^7.33.0",
"@sentry/browser": "^7.31.1",
"@sentry/tracing": "^7.31.1",
"@squoosh/cli": "^0.7.3",
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
"@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.7",
"@types/codemirror": "5.60.6",
"@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"@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.291",
"pyright": "^1.1.290",
"rapidoc": "^9.3.4",
"rollup": "^2.79.1",
"rollup-plugin-copy": "^3.4.0",

View File

@ -13,11 +13,9 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
import { t } from "@lingui/macro";
import { CSSResult, TemplateResult, html } from "lit";
import { 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 {
@ -34,7 +32,6 @@ export function BlueprintStatus(blueprint?: BlueprintInstance): string {
}
return t`Unknown`;
}
@customElement("ak-blueprint-list")
export class BlueprintListPage extends TablePage<BlueprintInstance> {
searchEnabled(): boolean {
@ -50,16 +47,11 @@ 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,
@ -104,34 +96,9 @@ 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`<div>${item.name}</div>
${description ? html`<small>${description}</small>` : html``}`,
html`${item.name}`,
html`${BlueprintStatus(item)}`,
html`${item.lastApplied.toLocaleString()}`,
html`<ak-label color=${item.enabled ? PFColor.Green : PFColor.Red}>

View File

@ -132,17 +132,6 @@ 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,6 +1,7 @@
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";
@ -34,7 +35,7 @@ export class PromptListPage extends TablePage<Prompt> {
checkbox = true;
@property()
order = "name";
order = "order";
async apiEndpoint(page: number): Promise<PaginatedResponse<Prompt>> {
return new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsList({
@ -47,8 +48,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`),
@ -80,8 +81,8 @@ export class PromptListPage extends TablePage<Prompt> {
row(item: Prompt): TemplateResult[] {
return [
html`${item.name}`,
html`<code>${item.fieldKey}</code>`,
html`${item.fieldKey}`,
html`${truncate(item.label, 20)}`,
html`${item.type}`,
html`${item.order}`,
html`${item.promptstageSet?.map((stage) => {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 344 KiB

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2023.1.2";
export const VERSION = "2023.1.3";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

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, state } from "lit/decorators.js";
import { customElement, property } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { until } from "lit/directives/until.js";
@ -44,7 +44,6 @@ import {
CapabilitiesEnum,
ChallengeChoices,
ChallengeTypes,
ContextualFlowInfo,
CurrentTenant,
FlowChallengeResponseRequest,
FlowErrorChallenge,
@ -96,28 +95,9 @@ export class FlowExecutor extends AKElement implements StageHost {
@property({ attribute: false })
tenant!: CurrentTenant;
@state()
@property({ attribute: false })
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[] {
@ -188,6 +168,14 @@ 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();
@ -210,9 +198,6 @@ 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;
}
@ -246,8 +231,9 @@ export class FlowExecutor extends AKElement implements StageHost {
);
}
this.challenge = challenge;
if (this.challenge.flowInfo) {
this.flowInfo = this.challenge.flowInfo;
// Only set background on first update, flow won't change throughout execution
if (this.challenge?.flowInfo?.background) {
this.setBackground(this.challenge.flowInfo.background);
}
})
.catch((e: Error | ResponseError) => {
@ -545,11 +531,13 @@ export class FlowExecutor extends AKElement implements StageHost {
>${t`Powered by authentik`}</a
>
</li>
${this.flowInfo?.background?.startsWith("/static")
${this.challenge?.flowInfo?.background?.startsWith(
"/static",
)
? html`
<li>
<a
href="https://unsplash.com/@saishmenon"
href="https://unsplash.com/@chrishenryphoto"
>${t`Background image`}</a
>
</li>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

View File

@ -1,151 +0,0 @@
---
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,8 +34,6 @@ 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,7 +60,3 @@ 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

@ -99,7 +99,7 @@ This includes the following:
- `context['application']`: The application the user is in the process of authorizing. (Optional)
- `context['source']`: The source the user is authenticating/enrolling with. (Optional)
- `context['pending_user']`: The currently pending user, see [User](../user-group/user.md#object-attributes)
- `context['is_restored']`: Set to `True` when the flow plan has been restored from a flow token, for example the user clicked a link to a flow which was sent by an email stage. (Optional)
- `context['is_restored']`: Contains the flow token when the flow plan was restored from a link, for example the user clicked a link to a flow which was sent by an email stage. (Optional)
- `context['auth_method']`: Authentication method (this value is set by password stages) (Optional)
Depending on method, `context['auth_method_args']` is also set.

View File

@ -0,0 +1,27 @@
# CVE-2023-26481
_Reported by [@fuomag9](https://github.com/fuomag9)_
## Insufficient user check in FlowTokens by Email stage
### Summary
Due to an insufficient access check, a recovery flow link that is created by an admin (or sent via email by an admin) can be used to set the password for any arbitrary user.
### Patches
authentik 2022.12.3, 2023.1.3, 2023.2.3 fix this issue.
### Impact
This attack is only possible if a recovery flow exists, which has both an Identification and an Email stage bound to it. If the flow has policies on the identification stage to skip it when the flow is restored (by checking `request.context['is_restored']`), the flow is not affected by this. With this flow in place, an administrator must create a recovery Link or send a recovery URL to the attacker, who can, due to the improper validation of the token create, set the password for any account.
### Workaround
It is recommended to upgrade to the patched version of authentik. Regardless, for custom recovery flows it is recommended to add a policy that checks if the flow is restored, and skips the identification stage.
### For more information
If you have any questions or comments about this advisory:
- Email us at [security@goauthentik.io](mailto:security@goauthentik.io)

View File

@ -48,15 +48,15 @@ module.exports = {
},
{
type: "dropdown",
label: `Version: latest`,
label: `Version: ${releases[0].replace(
/releases\/\d+\/v/,
""
)}`,
position: "right",
items: releases.map((release) => {
const subdomain = release
.replace(/releases\/\d+\/v/, "")
.replace(".", "-");
const label =
"Version: " +
release.replace(/releases\/\d+\/v/, "");
const version = release.replace(/releases\/\d+\/v/, "");
const subdomain = version.replace(".", "-");
const label = `Version: ${version}`;
return {
label: label,
href: `https://version-${subdomain}.goauthentik.io`,

View File

@ -13,7 +13,6 @@
"@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",
@ -5486,15 +5485,6 @@
"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",
@ -11522,9 +11512,9 @@
}
},
"node_modules/ua-parser-js": {
"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==",
"version": "0.7.32",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==",
"funding": [
{
"type": "opencollective",
@ -16559,12 +16549,6 @@
"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",
@ -20889,9 +20873,9 @@
"peer": true
},
"ua-parser-js": {
"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=="
"version": "0.7.32",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw=="
},
"unherit": {
"version": "1.1.3",

View File

@ -19,7 +19,6 @@
"@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

@ -298,9 +298,10 @@ module.exports = {
},
items: [
"security/policy",
"security/CVE-2022-23555",
"security/CVE-2022-46145",
"security/CVE-2022-46172",
"security/CVE-2022-23555",
"security/CVE-2023-26481",
],
},
],

View File

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

Some files were not shown because too many files have changed in this diff Show More