Compare commits
40 Commits
version-20
...
root/confi
Author | SHA1 | Date | |
---|---|---|---|
3fa987f443 | |||
5ea9595c9c | |||
1b6f920265 | |||
3bf8c915d5 | |||
1cc578be66 | |||
36f8f8bae5 | |||
68058fb2ae | |||
0abbe8288e | |||
a9de9101ca | |||
19ed9dc5e9 | |||
b5ae712f35 | |||
b1c01b53e7 | |||
5b31e47573 | |||
d2a9a294f2 | |||
32c7c58518 | |||
349c6e50c2 | |||
2e8027fa04 | |||
0bc1b33663 | |||
8564f9ef87 | |||
7cfd84d8f0 | |||
f2e40ec7e3 | |||
1f1255a829 | |||
53b65a9d1a | |||
9437e2d3ab | |||
6a7b78abc2 | |||
9c24e5195b | |||
306b046b9a | |||
10b50c5845 | |||
3912a57df2 | |||
08142d393f | |||
0f7af256d6 | |||
16076cc46f | |||
8aa16e66e4 | |||
a9b32e2f97 | |||
b2d272bf6f | |||
31ef6fb6a6 | |||
c9c059a008 | |||
a5e84b5482 | |||
7c697e09f3 | |||
b99afd82b2 |
@ -31,6 +31,16 @@ def validate_auth(header: bytes) -> Optional[str]:
|
||||
|
||||
|
||||
def bearer_auth(raw_header: bytes) -> Optional[User]:
|
||||
"""raw_header in the Format of `Bearer ....`"""
|
||||
user = auth_user_lookup(raw_header)
|
||||
if not user:
|
||||
return None
|
||||
if not user.is_active:
|
||||
raise AuthenticationFailed("Token invalid/expired")
|
||||
return user
|
||||
|
||||
|
||||
def auth_user_lookup(raw_header: bytes) -> Optional[User]:
|
||||
"""raw_header in the Format of `Bearer ....`"""
|
||||
from authentik.providers.oauth2.models import RefreshToken
|
||||
|
||||
|
@ -3,13 +3,12 @@ from base64 import b64encode
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from guardian.shortcuts import get_anonymous_user
|
||||
from rest_framework.exceptions import AuthenticationFailed
|
||||
|
||||
from authentik.api.authentication import bearer_auth
|
||||
from authentik.blueprints.tests import reconcile_app
|
||||
from authentik.core.models import USER_ATTRIBUTE_SA, Token, TokenIntents
|
||||
from authentik.core.tests.utils import create_test_flow
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
|
||||
from authentik.providers.oauth2.models import OAuth2Provider, RefreshToken
|
||||
@ -36,9 +35,18 @@ class TestAPIAuth(TestCase):
|
||||
|
||||
def test_bearer_valid(self):
|
||||
"""Test valid token"""
|
||||
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=get_anonymous_user())
|
||||
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=create_test_admin_user())
|
||||
self.assertEqual(bearer_auth(f"Bearer {token.key}".encode()), token.user)
|
||||
|
||||
def test_bearer_valid_deactivated(self):
|
||||
"""Test valid token"""
|
||||
user = create_test_admin_user()
|
||||
user.is_active = False
|
||||
user.save()
|
||||
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=user)
|
||||
with self.assertRaises(AuthenticationFailed):
|
||||
bearer_auth(f"Bearer {token.key}".encode())
|
||||
|
||||
def test_managed_outpost(self):
|
||||
"""Test managed outpost"""
|
||||
with self.assertRaises(AuthenticationFailed):
|
||||
@ -56,7 +64,7 @@ class TestAPIAuth(TestCase):
|
||||
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
|
||||
)
|
||||
refresh = RefreshToken.objects.create(
|
||||
user=get_anonymous_user(),
|
||||
user=create_test_admin_user(),
|
||||
provider=provider,
|
||||
refresh_token=generate_id(),
|
||||
_scope=SCOPE_AUTHENTIK_API,
|
||||
@ -69,7 +77,7 @@ class TestAPIAuth(TestCase):
|
||||
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
|
||||
)
|
||||
refresh = RefreshToken.objects.create(
|
||||
user=get_anonymous_user(),
|
||||
user=create_test_admin_user(),
|
||||
provider=provider,
|
||||
refresh_token=generate_id(),
|
||||
_scope="",
|
||||
|
@ -57,9 +57,10 @@ class AuthentikBlueprintsConfig(ManagedAppConfig):
|
||||
|
||||
def reconcile_blueprints_discover(self):
|
||||
"""Run blueprint discovery"""
|
||||
from authentik.blueprints.v1.tasks import blueprints_discover
|
||||
from authentik.blueprints.v1.tasks import blueprints_discover, clear_failed_blueprints
|
||||
|
||||
blueprints_discover.delay()
|
||||
clear_failed_blueprints.delay()
|
||||
|
||||
def import_models(self):
|
||||
super().import_models()
|
||||
|
@ -9,4 +9,9 @@ CELERY_BEAT_SCHEDULE = {
|
||||
"schedule": crontab(minute=fqdn_rand("blueprints_v1_discover"), hour="*"),
|
||||
"options": {"queue": "authentik_scheduled"},
|
||||
},
|
||||
"blueprints_v1_cleanup": {
|
||||
"task": "authentik.blueprints.v1.tasks.clear_failed_blueprints",
|
||||
"schedule": crontab(minute=fqdn_rand("blueprints_v1_cleanup"), hour="*"),
|
||||
"options": {"queue": "authentik_scheduled"},
|
||||
},
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ entries:
|
||||
pk: cb954fd4-65a5-4ad9-b1ee-180ee9559cf4
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
name: qwerweqrq
|
||||
field_key: username
|
||||
label: Username
|
||||
type: username
|
||||
|
@ -13,7 +13,7 @@ from authentik.tenants.models import Tenant
|
||||
class TestPackaged(TransactionTestCase):
|
||||
"""Empty class, test methods are added dynamically"""
|
||||
|
||||
@apply_blueprint("default/90-default-tenant.yaml")
|
||||
@apply_blueprint("default/default-tenant.yaml")
|
||||
def test_decorator_static(self):
|
||||
"""Test @apply_blueprint decorator"""
|
||||
self.assertTrue(Tenant.objects.filter(domain="authentik-default").exists())
|
||||
|
@ -262,15 +262,21 @@ class TestBlueprintsV1(TransactionTestCase):
|
||||
with transaction_rollback():
|
||||
# First stage fields
|
||||
username_prompt = Prompt.objects.create(
|
||||
field_key="username", label="Username", order=0, type=FieldTypes.TEXT
|
||||
name=generate_id(),
|
||||
field_key="username",
|
||||
label="Username",
|
||||
order=0,
|
||||
type=FieldTypes.TEXT,
|
||||
)
|
||||
password = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="password",
|
||||
label="Password",
|
||||
order=1,
|
||||
type=FieldTypes.PASSWORD,
|
||||
)
|
||||
password_repeat = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="password_repeat",
|
||||
label="Password (repeat)",
|
||||
order=2,
|
||||
|
@ -3,3 +3,4 @@
|
||||
LABEL_AUTHENTIK_SYSTEM = "blueprints.goauthentik.io/system"
|
||||
LABEL_AUTHENTIK_INSTANTIATE = "blueprints.goauthentik.io/instantiate"
|
||||
LABEL_AUTHENTIK_GENERATED = "blueprints.goauthentik.io/generated"
|
||||
LABEL_AUTHENTIK_DESCRIPTION = "blueprints.goauthentik.io/description"
|
||||
|
@ -219,3 +219,14 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
|
||||
finally:
|
||||
if instance:
|
||||
instance.save()
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
def clear_failed_blueprints():
|
||||
"""Remove blueprints which couldn't be fetched"""
|
||||
# Exclude OCI blueprints as those might be temporarily unavailable
|
||||
for blueprint in BlueprintInstance.objects.exclude(path__startswith="oci://"):
|
||||
try:
|
||||
blueprint.retrieve()
|
||||
except BlueprintRetrievalFailed:
|
||||
blueprint.delete()
|
||||
|
@ -4,6 +4,8 @@ from json import loads
|
||||
from typing import Any, Optional
|
||||
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.contrib.sessions.backends.cache import KEY_PREFIX
|
||||
from django.core.cache import cache
|
||||
from django.db.models.functions import ExtractHour
|
||||
from django.db.models.query import QuerySet
|
||||
from django.db.transaction import atomic
|
||||
@ -57,6 +59,7 @@ from authentik.core.models import (
|
||||
USER_ATTRIBUTE_SA,
|
||||
USER_ATTRIBUTE_TOKEN_EXPIRING,
|
||||
USER_PATH_SERVICE_ACCOUNT,
|
||||
AuthenticatedSession,
|
||||
Group,
|
||||
Token,
|
||||
TokenIntents,
|
||||
@ -561,3 +564,14 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def partial_update(self, request: Request, *args, **kwargs) -> Response:
|
||||
response = super().partial_update(request, *args, **kwargs)
|
||||
instance: User = self.get_object()
|
||||
if not instance.is_active:
|
||||
sessions = AuthenticatedSession.objects.filter(user=instance)
|
||||
session_ids = sessions.values_list("session_key", flat=True)
|
||||
cache.delete_many(f"{KEY_PREFIX}{session}" for session in session_ids)
|
||||
sessions.delete()
|
||||
LOGGER.debug("Deleted user's sessions", user=instance.username)
|
||||
return response
|
||||
|
@ -1,10 +1,12 @@
|
||||
"""Test Users API"""
|
||||
from json import loads
|
||||
|
||||
from django.contrib.sessions.backends.cache import KEY_PREFIX
|
||||
from django.core.cache import cache
|
||||
from django.urls.base import reverse
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.core.models import AuthenticatedSession, User
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_tenant
|
||||
from authentik.flows.models import FlowDesignation
|
||||
from authentik.lib.config import CONFIG
|
||||
@ -257,3 +259,26 @@ class TestUsersAPI(APITestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = loads(response.content.decode())
|
||||
self.assertEqual(body["user"]["avatar"], "bar")
|
||||
|
||||
def test_session_delete(self):
|
||||
"""Ensure sessions are deleted when a user is deactivated"""
|
||||
user = create_test_admin_user()
|
||||
session_id = generate_id()
|
||||
AuthenticatedSession.objects.create(
|
||||
user=user,
|
||||
session_key=session_id,
|
||||
last_ip="",
|
||||
)
|
||||
cache.set(KEY_PREFIX + session_id, "foo")
|
||||
|
||||
self.client.force_login(self.admin)
|
||||
response = self.client.patch(
|
||||
reverse("authentik_api:user-detail", kwargs={"pk": user.pk}),
|
||||
data={
|
||||
"is_active": False,
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.assertIsNone(cache.get(KEY_PREFIX + session_id))
|
||||
self.assertFalse(AuthenticatedSession.objects.filter(session_key=session_id).exists())
|
||||
|
@ -5,13 +5,20 @@ from contextlib import contextmanager
|
||||
from glob import glob
|
||||
from json import dumps, loads
|
||||
from json.decoder import JSONDecodeError
|
||||
from pathlib import Path
|
||||
from sys import argv, stderr
|
||||
from time import time
|
||||
from typing import Any
|
||||
from typing import Any, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import yaml
|
||||
from django.conf import ImproperlyConfigured
|
||||
from watchdog.events import (
|
||||
FileModifiedEvent,
|
||||
FileSystemEvent,
|
||||
FileSystemEventHandler,
|
||||
)
|
||||
from watchdog.observers import Observer
|
||||
|
||||
SEARCH_PATHS = ["authentik/lib/default.yml", "/etc/authentik/config.yml", ""] + glob(
|
||||
"/etc/authentik/config.d/*.yml", recursive=True
|
||||
@ -38,9 +45,47 @@ class ConfigLoader:
|
||||
A variable like AUTHENTIK_POSTGRESQL__HOST would translate to postgresql.host"""
|
||||
|
||||
loaded_file = []
|
||||
observer: Observer
|
||||
|
||||
class FSObserver(FileSystemEventHandler):
|
||||
"""File system observer"""
|
||||
|
||||
loader: "ConfigLoader"
|
||||
path: str
|
||||
container: Optional[dict] = None
|
||||
key: Optional[str] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
loader: "ConfigLoader",
|
||||
path: str,
|
||||
container: Optional[dict] = None,
|
||||
key: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.loader = loader
|
||||
self.path = path
|
||||
self.container = container
|
||||
self.key = key
|
||||
|
||||
def on_any_event(self, event: FileSystemEvent):
|
||||
if not isinstance(event, FileModifiedEvent):
|
||||
return
|
||||
if event.is_directory:
|
||||
return
|
||||
if event.src_path != self.path:
|
||||
return
|
||||
if self.container and self.key:
|
||||
with open(self.path, "r", encoding="utf8") as _file:
|
||||
self.container[self.key] = _file.read()
|
||||
else:
|
||||
self.loader.log("info", "Updating from changed file", file=self.path)
|
||||
self.loader.update_from_file(self.path, watch=False)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.observer = Observer()
|
||||
self.observer.start()
|
||||
self.__config = {}
|
||||
base_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), "../.."))
|
||||
for path in SEARCH_PATHS:
|
||||
@ -81,11 +126,11 @@ class ConfigLoader:
|
||||
root[key] = self.update(root.get(key, {}), value)
|
||||
else:
|
||||
if isinstance(value, str):
|
||||
value = self.parse_uri(value)
|
||||
value = self.parse_uri(value, root, key)
|
||||
root[key] = value
|
||||
return root
|
||||
|
||||
def parse_uri(self, value: str) -> str:
|
||||
def parse_uri(self, value: str, container: dict[str, Any], key: Optional[str] = None, ) -> str:
|
||||
"""Parse string values which start with a URI"""
|
||||
url = urlparse(value)
|
||||
if url.scheme == "env":
|
||||
@ -93,13 +138,23 @@ class ConfigLoader:
|
||||
if url.scheme == "file":
|
||||
try:
|
||||
with open(url.path, "r", encoding="utf8") as _file:
|
||||
value = _file.read().strip()
|
||||
value = _file.read()
|
||||
if key:
|
||||
self.observer.schedule(
|
||||
ConfigLoader.FSObserver(
|
||||
self,
|
||||
url.path,
|
||||
container,
|
||||
key,
|
||||
),
|
||||
Path(url.path).parent,
|
||||
)
|
||||
except OSError as exc:
|
||||
self.log("error", f"Failed to read config value from {url.path}: {exc}")
|
||||
value = url.query
|
||||
return value
|
||||
|
||||
def update_from_file(self, path: str):
|
||||
def update_from_file(self, path: str, watch=True):
|
||||
"""Update config from file contents"""
|
||||
try:
|
||||
with open(path, encoding="utf8") as file:
|
||||
@ -107,6 +162,8 @@ class ConfigLoader:
|
||||
self.update(self.__config, yaml.safe_load(file))
|
||||
self.log("debug", "Loaded config", file=path)
|
||||
self.loaded_file.append(path)
|
||||
if watch:
|
||||
self.observer.schedule(ConfigLoader.FSObserver(self, path), Path(path).parent)
|
||||
except yaml.YAMLError as exc:
|
||||
raise ImproperlyConfigured from exc
|
||||
except PermissionError as exc:
|
||||
@ -181,13 +238,12 @@ class ConfigLoader:
|
||||
if comp not in root:
|
||||
root[comp] = {}
|
||||
root = root.get(comp, {})
|
||||
root[path_parts[-1]] = value
|
||||
self.parse_uri(value, root, path_parts[-1])
|
||||
|
||||
def y_bool(self, path: str, default=False) -> bool:
|
||||
"""Wrapper for y that converts value into boolean"""
|
||||
return str(self.y(path, default)).lower() == "true"
|
||||
|
||||
|
||||
CONFIG = ConfigLoader()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -5,7 +5,7 @@ from tempfile import mkstemp
|
||||
from django.conf import ImproperlyConfigured
|
||||
from django.test import TestCase
|
||||
|
||||
from authentik.lib.config import ENV_PREFIX, ConfigLoader
|
||||
from authentik.lib.config import CONFIG, ENV_PREFIX, ConfigLoader
|
||||
|
||||
|
||||
class TestConfig(TestCase):
|
||||
@ -31,8 +31,8 @@ class TestConfig(TestCase):
|
||||
"""Test URI parsing (environment)"""
|
||||
config = ConfigLoader()
|
||||
environ["foo"] = "bar"
|
||||
self.assertEqual(config.parse_uri("env://foo"), "bar")
|
||||
self.assertEqual(config.parse_uri("env://foo?bar"), "bar")
|
||||
self.assertEqual(config.parse_uri("env://foo", {}), "bar")
|
||||
self.assertEqual(config.parse_uri("env://foo?bar", {}), "bar")
|
||||
|
||||
def test_uri_file(self):
|
||||
"""Test URI parsing (file load)"""
|
||||
@ -41,8 +41,8 @@ class TestConfig(TestCase):
|
||||
write(file, "foo".encode())
|
||||
_, file2_name = mkstemp()
|
||||
chmod(file2_name, 0o000) # Remove all permissions so we can't read the file
|
||||
self.assertEqual(config.parse_uri(f"file://{file_name}"), "foo")
|
||||
self.assertEqual(config.parse_uri(f"file://{file2_name}?def"), "def")
|
||||
self.assertEqual(config.parse_uri(f"file://{file_name}", {}), "foo")
|
||||
self.assertEqual(config.parse_uri(f"file://{file2_name}?def", {}), "def")
|
||||
unlink(file_name)
|
||||
unlink(file2_name)
|
||||
|
||||
@ -59,3 +59,13 @@ class TestConfig(TestCase):
|
||||
config.update_from_file(file2_name)
|
||||
unlink(file_name)
|
||||
unlink(file2_name)
|
||||
|
||||
def test_update(self):
|
||||
"""Test change to file"""
|
||||
file, file_name = mkstemp()
|
||||
write(file, b"test")
|
||||
CONFIG.y_set("test.file", f"file://{file_name}")
|
||||
self.assertEqual(CONFIG.y("test.file"), "test")
|
||||
write(file, "test2")
|
||||
self.assertEqual(CONFIG.y("test.file"), "test2")
|
||||
unlink(file_name)
|
||||
|
@ -183,7 +183,7 @@ class DockerController(BaseController):
|
||||
try:
|
||||
self.client.images.pull(image)
|
||||
except DockerException: # pragma: no cover
|
||||
image = f"goauthentik.io/{self.outpost.type}:latest"
|
||||
image = f"ghcr.io/goauthentik/{self.outpost.type}:latest"
|
||||
self.client.images.pull(image)
|
||||
return image
|
||||
|
||||
|
@ -4,6 +4,7 @@ from django.urls.base import reverse
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.policies.password.models import PasswordPolicy
|
||||
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
||||
|
||||
@ -16,6 +17,7 @@ class TestPasswordPolicyFlow(FlowTestCase):
|
||||
self.flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
||||
|
||||
password_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="password",
|
||||
label="PASSWORD_LABEL",
|
||||
type=FieldTypes.PASSWORD,
|
||||
|
@ -376,7 +376,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
||||
|
||||
def challenge_valid(self, response: AuthenticatorValidationChallengeResponse) -> HttpResponse:
|
||||
# All validation is done by the serializer
|
||||
user = self.get_pending_user()
|
||||
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
|
||||
if not user:
|
||||
if "webauthn" not in response.data:
|
||||
return self.executor.stage_invalid()
|
||||
|
@ -9,9 +9,10 @@ from webauthn.helpers.bytes_to_base64url import bytes_to_base64url
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.flows.models import FlowStageBinding, NotConfiguredAction
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||
from authentik.flows.stage import StageView
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.flows.views.executor import FlowExecutorView
|
||||
from authentik.flows.views.executor import SESSION_KEY_PLAN, FlowExecutorView
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.tests.utils import get_request
|
||||
from authentik.stages.authenticator_validate.challenge import (
|
||||
@ -20,10 +21,14 @@ from authentik.stages.authenticator_validate.challenge import (
|
||||
validate_challenge_webauthn,
|
||||
)
|
||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||
from authentik.stages.authenticator_validate.stage import AuthenticatorValidateStageView
|
||||
from authentik.stages.authenticator_validate.stage import (
|
||||
SESSION_KEY_DEVICE_CHALLENGES,
|
||||
AuthenticatorValidateStageView,
|
||||
)
|
||||
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
|
||||
from authentik.stages.authenticator_webauthn.stage import SESSION_KEY_WEBAUTHN_CHALLENGE
|
||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||
from authentik.stages.user_login.models import UserLoginStage
|
||||
|
||||
|
||||
class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
@ -185,10 +190,7 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
|
||||
def test_validate_challenge(self):
|
||||
"""Test webauthn"""
|
||||
request = get_request("/")
|
||||
request.user = self.user
|
||||
|
||||
WebAuthnDevice.objects.create(
|
||||
device = WebAuthnDevice.objects.create(
|
||||
user=self.user,
|
||||
public_key=(
|
||||
"pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J"
|
||||
@ -204,49 +206,134 @@ class AuthenticatorValidateStageWebAuthnTests(FlowTestCase):
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.WEBAUTHN],
|
||||
)
|
||||
stage_view = AuthenticatorValidateStageView(
|
||||
FlowExecutorView(flow=flow, current_stage=stage), request=request
|
||||
)
|
||||
request = get_request("/")
|
||||
request.session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes(
|
||||
session = self.client.session
|
||||
plan = FlowPlan(flow_pk=flow.pk.hex)
|
||||
plan.append_stage(stage)
|
||||
plan.append_stage(UserLoginStage(name=generate_id()))
|
||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session[SESSION_KEY_DEVICE_CHALLENGES] = [
|
||||
{
|
||||
"device_class": device.__class__.__name__.lower().replace("device", ""),
|
||||
"device_uid": device.pk,
|
||||
"challenge": {},
|
||||
}
|
||||
]
|
||||
session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes(
|
||||
(
|
||||
"g98I51mQvZXo5lxLfhrD2zfolhZbLRyCgqkkYap1"
|
||||
"jwSaJ13BguoJWCF9_Lg3AgO4Wh-Bqa556JE20oKsYbl6RA"
|
||||
)
|
||||
)
|
||||
request.session.save()
|
||||
session.save()
|
||||
|
||||
stage_view = AuthenticatorValidateStageView(
|
||||
FlowExecutorView(flow=flow, current_stage=stage), request=request
|
||||
)
|
||||
request.META["SERVER_NAME"] = "localhost"
|
||||
request.META["SERVER_PORT"] = "9000"
|
||||
validate_challenge_webauthn(
|
||||
{
|
||||
"id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
|
||||
"rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
|
||||
"type": "public-key",
|
||||
"assertionClientExtensions": "{}",
|
||||
"response": {
|
||||
"clientDataJSON": (
|
||||
"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4TGZo"
|
||||
"ckQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdoLUJxYTU1"
|
||||
"NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjkwMDAiLCJjcm9z"
|
||||
"c09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUiOiJkbyBub3Qg"
|
||||
"Y29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczov"
|
||||
"L2dvby5nbC95YWJQZXgifQ==",
|
||||
),
|
||||
"signature": (
|
||||
"MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI"
|
||||
"AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag=="
|
||||
),
|
||||
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==",
|
||||
"userHandle": None,
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
data={
|
||||
"webauthn": {
|
||||
"id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
|
||||
"rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
|
||||
"type": "public-key",
|
||||
"assertionClientExtensions": "{}",
|
||||
"response": {
|
||||
"clientDataJSON": (
|
||||
"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4T"
|
||||
"GZockQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdoLU"
|
||||
"JxYTU1NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjkwMDA"
|
||||
"iLCJjcm9zc09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUi"
|
||||
"OiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuI"
|
||||
"FNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ==",
|
||||
),
|
||||
"signature": (
|
||||
"MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI"
|
||||
"AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag=="
|
||||
),
|
||||
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==",
|
||||
"userHandle": None,
|
||||
},
|
||||
},
|
||||
},
|
||||
stage_view,
|
||||
self.user,
|
||||
SERVER_NAME="localhost",
|
||||
SERVER_PORT="9000",
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
)
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
|
||||
def test_validate_challenge_userless(self):
|
||||
"""Test webauthn"""
|
||||
device = WebAuthnDevice.objects.create(
|
||||
user=self.user,
|
||||
public_key=(
|
||||
"pQECAyYgASFYIGsBLkklToCQkT7qJT_bJYN1sEc1oJdbnmoOc43i0J"
|
||||
"H6IlggLTXytuhzFVYYAK4PQNj8_coGrbbzSfUxdiPAcZTQCyU"
|
||||
),
|
||||
credential_id="QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
|
||||
sign_count=4,
|
||||
rp_id=generate_id(),
|
||||
)
|
||||
flow = create_test_flow()
|
||||
stage = AuthenticatorValidateStage.objects.create(
|
||||
name=generate_id(),
|
||||
not_configured_action=NotConfiguredAction.CONFIGURE,
|
||||
device_classes=[DeviceClasses.WEBAUTHN],
|
||||
)
|
||||
session = self.client.session
|
||||
plan = FlowPlan(flow_pk=flow.pk.hex)
|
||||
plan.append_stage(stage)
|
||||
plan.append_stage(UserLoginStage(name=generate_id()))
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session[SESSION_KEY_DEVICE_CHALLENGES] = [
|
||||
{
|
||||
"device_class": device.__class__.__name__.lower().replace("device", ""),
|
||||
"device_uid": device.pk,
|
||||
"challenge": {},
|
||||
}
|
||||
]
|
||||
session[SESSION_KEY_WEBAUTHN_CHALLENGE] = base64url_to_bytes(
|
||||
(
|
||||
"g98I51mQvZXo5lxLfhrD2zfolhZbLRyCgqkkYap1"
|
||||
"jwSaJ13BguoJWCF9_Lg3AgO4Wh-Bqa556JE20oKsYbl6RA"
|
||||
)
|
||||
)
|
||||
session.save()
|
||||
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
data={
|
||||
"webauthn": {
|
||||
"id": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
|
||||
"rawId": "QKZ97ASJAOIDyipAs6mKUxDUZgDrWrbAsUb5leL7-oU",
|
||||
"type": "public-key",
|
||||
"assertionClientExtensions": "{}",
|
||||
"response": {
|
||||
"clientDataJSON": (
|
||||
"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZzk4STUxbVF2WlhvNWx4T"
|
||||
"GZockQyemZvbGhaYkxSeUNncWtrWWFwMWp3U2FKMTNCZ3VvSldDRjlfTGczQWdPNFdoLU"
|
||||
"JxYTU1NkpFMjBvS3NZYmw2UkEiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjkwMDA"
|
||||
"iLCJjcm9zc09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUi"
|
||||
"OiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuI"
|
||||
"FNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ==",
|
||||
),
|
||||
"signature": (
|
||||
"MEQCIFNlrHf9ablJAalXLWkrqvHB8oIu8kwvRpH3X3rbJVpI"
|
||||
"AiAqtOK6mIZPk62kZN0OzFsHfuvu_RlOl7zlqSNzDdz_Ag=="
|
||||
),
|
||||
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAABQ==",
|
||||
"userHandle": None,
|
||||
},
|
||||
},
|
||||
},
|
||||
SERVER_NAME="localhost",
|
||||
SERVER_PORT="9000",
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||
)
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
|
||||
def test_validate_challenge_invalid(self):
|
||||
"""Test webauthn"""
|
||||
|
@ -42,6 +42,7 @@ class PromptSerializer(ModelSerializer):
|
||||
model = Prompt
|
||||
fields = [
|
||||
"pk",
|
||||
"name",
|
||||
"field_key",
|
||||
"label",
|
||||
"type",
|
||||
@ -59,5 +60,5 @@ class PromptViewSet(UsedByMixin, ModelViewSet):
|
||||
|
||||
queryset = Prompt.objects.all().prefetch_related("promptstage_set")
|
||||
serializer_class = PromptSerializer
|
||||
filterset_fields = ["field_key", "label", "type", "placeholder"]
|
||||
search_fields = ["field_key", "label", "type", "placeholder"]
|
||||
filterset_fields = ["field_key", "name", "label", "type", "placeholder"]
|
||||
search_fields = ["field_key", "name", "label", "type", "placeholder"]
|
||||
|
40
authentik/stages/prompt/migrations/0009_prompt_name.py
Normal file
40
authentik/stages/prompt/migrations/0009_prompt_name.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Generated by Django 4.1.5 on 2023-01-23 19:42
|
||||
|
||||
from django.apps.registry import Apps
|
||||
from django.db import migrations, models
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
|
||||
def set_generated_name(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
Prompt = apps.get_model("authentik_stages_prompt", "prompt")
|
||||
|
||||
for prompt in Prompt.objects.using(db_alias).all():
|
||||
name = prompt.field_key
|
||||
stage = prompt.promptstage_set.order_by("name").first()
|
||||
if stage:
|
||||
name += "_" + stage.name
|
||||
prompt.name = name
|
||||
prompt.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_prompt", "0008_alter_prompt_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="prompt",
|
||||
name="name",
|
||||
field=models.TextField(default="", unique=False, db_index=False, blank=False),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.RunPython(code=set_generated_name),
|
||||
migrations.AlterField(
|
||||
model_name="prompt",
|
||||
name="name",
|
||||
field=models.TextField(unique=True),
|
||||
),
|
||||
]
|
@ -96,6 +96,7 @@ class Prompt(SerializerModel):
|
||||
"""Single Prompt, part of a prompt stage."""
|
||||
|
||||
prompt_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
name = models.TextField(unique=True, blank=False)
|
||||
|
||||
field_key = models.TextField(
|
||||
help_text=_("Name of the form field, also used to store the value")
|
||||
|
@ -30,6 +30,7 @@ class TestPromptStage(FlowTestCase):
|
||||
self.factory = RequestFactory()
|
||||
self.flow = create_test_flow()
|
||||
username_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="username_prompt",
|
||||
label="USERNAME_LABEL",
|
||||
type=FieldTypes.USERNAME,
|
||||
@ -37,6 +38,7 @@ class TestPromptStage(FlowTestCase):
|
||||
placeholder="USERNAME_PLACEHOLDER",
|
||||
)
|
||||
text_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="text_prompt",
|
||||
label="TEXT_LABEL",
|
||||
type=FieldTypes.TEXT,
|
||||
@ -44,6 +46,7 @@ class TestPromptStage(FlowTestCase):
|
||||
placeholder="TEXT_PLACEHOLDER",
|
||||
)
|
||||
email_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="email_prompt",
|
||||
label="EMAIL_LABEL",
|
||||
type=FieldTypes.EMAIL,
|
||||
@ -51,6 +54,7 @@ class TestPromptStage(FlowTestCase):
|
||||
placeholder="EMAIL_PLACEHOLDER",
|
||||
)
|
||||
password_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="password_prompt",
|
||||
label="PASSWORD_LABEL",
|
||||
type=FieldTypes.PASSWORD,
|
||||
@ -58,6 +62,7 @@ class TestPromptStage(FlowTestCase):
|
||||
placeholder="PASSWORD_PLACEHOLDER",
|
||||
)
|
||||
password2_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="password2_prompt",
|
||||
label="PASSWORD_LABEL",
|
||||
type=FieldTypes.PASSWORD,
|
||||
@ -65,6 +70,7 @@ class TestPromptStage(FlowTestCase):
|
||||
placeholder="PASSWORD_PLACEHOLDER",
|
||||
)
|
||||
number_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="number_prompt",
|
||||
label="NUMBER_LABEL",
|
||||
type=FieldTypes.NUMBER,
|
||||
@ -72,12 +78,14 @@ class TestPromptStage(FlowTestCase):
|
||||
placeholder="NUMBER_PLACEHOLDER",
|
||||
)
|
||||
hidden_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="hidden_prompt",
|
||||
type=FieldTypes.HIDDEN,
|
||||
required=True,
|
||||
placeholder="HIDDEN_PLACEHOLDER",
|
||||
)
|
||||
static_prompt = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="static_prompt",
|
||||
type=FieldTypes.STATIC,
|
||||
required=True,
|
||||
|
@ -17,9 +17,10 @@ entries:
|
||||
placeholder_expression: false
|
||||
required: true
|
||||
type: text
|
||||
identifiers:
|
||||
field_key: username
|
||||
label: Username
|
||||
identifiers:
|
||||
name: default-source-enrollment-field-username
|
||||
id: prompt-field-username
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
@ -21,9 +21,10 @@ entries:
|
||||
placeholder_expression: true
|
||||
required: true
|
||||
type: text
|
||||
identifiers:
|
||||
field_key: username
|
||||
label: Username
|
||||
identifiers:
|
||||
name: default-user-settings-field-username
|
||||
id: prompt-field-username
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
@ -36,9 +37,10 @@ entries:
|
||||
placeholder_expression: true
|
||||
required: true
|
||||
type: text
|
||||
identifiers:
|
||||
field_key: name
|
||||
label: Name
|
||||
identifiers:
|
||||
name: default-user-settings-field-name
|
||||
id: prompt-field-name
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
@ -51,9 +53,10 @@ entries:
|
||||
placeholder_expression: true
|
||||
required: true
|
||||
type: email
|
||||
identifiers:
|
||||
field_key: email
|
||||
label: Email
|
||||
identifiers:
|
||||
name: default-user-settings-field-email
|
||||
id: prompt-field-email
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
@ -66,9 +69,10 @@ entries:
|
||||
placeholder_expression: true
|
||||
required: true
|
||||
type: ak-locale
|
||||
identifiers:
|
||||
field_key: attributes.settings.locale
|
||||
label: Locale
|
||||
identifiers:
|
||||
name: default-user-settings-field-locale
|
||||
id: prompt-field-locale
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
@ -19,10 +19,11 @@ entries:
|
||||
required: true
|
||||
sub_text: ''
|
||||
type: static
|
||||
id: prompt-field-header
|
||||
identifiers:
|
||||
field_key: oobe-header-text
|
||||
label: oobe-header-text
|
||||
id: prompt-field-header
|
||||
identifiers:
|
||||
name: initial-setup-field-header
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
order: 101
|
||||
@ -31,10 +32,11 @@ entries:
|
||||
required: true
|
||||
sub_text: ''
|
||||
type: email
|
||||
field_key: email
|
||||
label: Email
|
||||
id: prompt-field-email
|
||||
identifiers:
|
||||
field_key: admin_email
|
||||
label: Email
|
||||
name: initial-setup-field-email
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
order: 300
|
||||
@ -43,10 +45,11 @@ entries:
|
||||
required: true
|
||||
sub_text: ''
|
||||
type: password
|
||||
id: prompt-field-password
|
||||
identifiers:
|
||||
field_key: password
|
||||
label: Password
|
||||
id: prompt-field-password
|
||||
identifiers:
|
||||
name: initial-setup-field-password
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
order: 301
|
||||
@ -55,10 +58,11 @@ entries:
|
||||
required: true
|
||||
sub_text: ''
|
||||
type: password
|
||||
id: prompt-field-password-repeat
|
||||
identifiers:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
id: prompt-field-password-repeat
|
||||
identifiers:
|
||||
name: initial-setup-field-password-repeat
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
expression: |
|
||||
@ -66,8 +70,6 @@ entries:
|
||||
# by injecting "pending_user"
|
||||
akadmin = ak_user_by(username="akadmin")
|
||||
context["flow_plan"].context["pending_user"] = akadmin
|
||||
# Remap the email value
|
||||
context["prompt_data"]["email"] = context["prompt_data"]["admin_email"]
|
||||
return True
|
||||
id: policy-default-oobe-prefill-user
|
||||
identifiers:
|
@ -17,9 +17,10 @@ entries:
|
||||
placeholder_expression: false
|
||||
required: true
|
||||
type: password
|
||||
identifiers:
|
||||
field_key: password
|
||||
label: Password
|
||||
identifiers:
|
||||
name: default-password-change-field-password
|
||||
id: prompt-field-password
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
||||
@ -28,9 +29,10 @@ entries:
|
||||
placeholder_expression: false
|
||||
required: true
|
||||
type: password
|
||||
identifiers:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
identifiers:
|
||||
name: default-password-change-field-password-repeat
|
||||
id: prompt-field-password-repeat
|
||||
model: authentik_stages_prompt.prompt
|
||||
- attrs:
|
@ -13,56 +13,61 @@ entries:
|
||||
title: Welcome to authentik!
|
||||
designation: enrollment
|
||||
authentication: require_unauthenticated
|
||||
- identifiers:
|
||||
- id: prompt-field-username
|
||||
model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: default-enrollment-field-username
|
||||
attrs:
|
||||
field_key: username
|
||||
label: Username
|
||||
id: prompt-field-username
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
type: username
|
||||
required: true
|
||||
placeholder: Username
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
- identifiers:
|
||||
field_key: password
|
||||
label: Password
|
||||
name: default-enrollment-field-password
|
||||
id: prompt-field-password
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password
|
||||
label: Password
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
- identifiers:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
name: default-enrollment-field-password-repeat
|
||||
id: prompt-field-password-repeat
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password (repeat)
|
||||
placeholder_expression: false
|
||||
order: 1
|
||||
- identifiers:
|
||||
field_key: name
|
||||
label: Name
|
||||
name: default-enrollment-field-name
|
||||
id: prompt-field-name
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: name
|
||||
label: Name
|
||||
type: text
|
||||
required: true
|
||||
placeholder: Name
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
- identifiers:
|
||||
field_key: email
|
||||
label: Email
|
||||
name: default-enrollment-field-email
|
||||
id: prompt-field-email
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: email
|
||||
label: Email
|
||||
type: email
|
||||
required: true
|
||||
placeholder: Email
|
||||
|
@ -14,55 +14,60 @@ entries:
|
||||
designation: enrollment
|
||||
authentication: require_unauthenticated
|
||||
- identifiers:
|
||||
field_key: username
|
||||
label: Username
|
||||
name: default-enrollment-field-username
|
||||
id: prompt-field-username
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: username
|
||||
label: Username
|
||||
type: username
|
||||
required: true
|
||||
placeholder: Username
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
- identifiers:
|
||||
field_key: password
|
||||
label: Password
|
||||
name: default-enrollment-field-password
|
||||
id: prompt-field-password
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password
|
||||
label: Password
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
- identifiers:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
name: default-enrollment-field-password-repeat
|
||||
id: prompt-field-password-repeat
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password (repeat)
|
||||
placeholder_expression: false
|
||||
order: 1
|
||||
- identifiers:
|
||||
field_key: name
|
||||
label: Name
|
||||
name: default-enrollment-field-name
|
||||
id: prompt-field-name
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: name
|
||||
label: Name
|
||||
type: text
|
||||
required: true
|
||||
placeholder: Name
|
||||
placeholder_expression: false
|
||||
order: 0
|
||||
- identifiers:
|
||||
field_key: email
|
||||
label: Email
|
||||
name: default-enrollment-field-email
|
||||
id: prompt-field-email
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: email
|
||||
label: Email
|
||||
type: email
|
||||
required: true
|
||||
placeholder: Email
|
||||
|
@ -14,22 +14,24 @@ entries:
|
||||
designation: recovery
|
||||
authentication: require_unauthenticated
|
||||
- identifiers:
|
||||
field_key: password
|
||||
label: Password
|
||||
name: default-recovery-field-password
|
||||
id: prompt-field-password
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password
|
||||
label: Password
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password
|
||||
order: 0
|
||||
placeholder_expression: false
|
||||
- identifiers:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
name: default-recovery-field-password-repeat
|
||||
id: prompt-field-password-repeat
|
||||
model: authentik_stages_prompt.prompt
|
||||
attrs:
|
||||
field_key: password_repeat
|
||||
label: Password (repeat)
|
||||
type: password
|
||||
required: true
|
||||
placeholder: Password (repeat)
|
||||
|
70
blueprints/migrations/migrate-prompt-name.yaml
Normal file
70
blueprints/migrations/migrate-prompt-name.yaml
Normal file
@ -0,0 +1,70 @@
|
||||
version: 1
|
||||
metadata:
|
||||
labels:
|
||||
blueprints.goauthentik.io/description: Migrate to 2023.2, remove unused prompt fields
|
||||
name: Migration - Remove old prompt fields
|
||||
entries:
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: admin_email_stage-default-oobe-password
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: attributes.settings.locale_default-user-settings
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: email_default-user-settings
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: name_default-user-settings
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: oobe-header-text_stage-default-oobe-password
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: password_default-password-change-prompt
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: password_repeat_default-password-change-prompt
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
||||
- model: authentik_stages_prompt.prompt
|
||||
identifiers:
|
||||
name: username_default-source-enrollment-prompt
|
||||
attrs:
|
||||
field_key: foo
|
||||
label: foo
|
||||
type: text
|
||||
state: absent
|
2
go.mod
2
go.mod
@ -25,7 +25,7 @@ require (
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
goauthentik.io/api/v3 v3.2023010.1
|
||||
goauthentik.io/api/v3 v3.2023012.2
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
||||
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b
|
||||
|
4
go.sum
4
go.sum
@ -382,8 +382,8 @@ go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZp
|
||||
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
|
||||
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
goauthentik.io/api/v3 v3.2023010.1 h1:DQuf201pD+OKVnzpLKCaKw8IS397ewzMzFIGXyhmQ3w=
|
||||
goauthentik.io/api/v3 v3.2023010.1/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
|
||||
goauthentik.io/api/v3 v3.2023012.2 h1:aP/JlZCxGaPuV+vPabL3Niz7lWAYnNolrINYSFBDFG0=
|
||||
goauthentik.io/api/v3 v3.2023012.2/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
|
@ -1,8 +1,11 @@
|
||||
package ak
|
||||
|
||||
import "context"
|
||||
|
||||
type Outpost interface {
|
||||
Start() error
|
||||
Stop() error
|
||||
Refresh() error
|
||||
TimerFlowCacheExpiry()
|
||||
TimerFlowCacheExpiry(context.Context)
|
||||
Type() string
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
package ak
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (a *APIController) startPeriodicalTasks() {
|
||||
go a.Server.TimerFlowCacheExpiry()
|
||||
go func() {
|
||||
for range time.Tick(time.Duration(a.GlobalConfig.CacheTimeoutFlows) * time.Second) {
|
||||
a.logger.WithField("timer", "cache-timeout").Debug("Running periodical tasks")
|
||||
a.Server.TimerFlowCacheExpiry()
|
||||
}
|
||||
}()
|
||||
ctx, canc := context.WithCancel(context.Background())
|
||||
defer canc()
|
||||
go a.Server.TimerFlowCacheExpiry(ctx)
|
||||
for range time.Tick(time.Duration(a.GlobalConfig.CacheTimeoutFlows) * time.Second) {
|
||||
a.logger.WithField("timer", "cache-timeout").Debug("Running periodical tasks")
|
||||
a.Server.TimerFlowCacheExpiry(ctx)
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +143,10 @@ func (fe *FlowExecutor) GetSession() *http.Cookie {
|
||||
return fe.session
|
||||
}
|
||||
|
||||
func (fe *FlowExecutor) SetSession(s *http.Cookie) {
|
||||
fe.session = s
|
||||
}
|
||||
|
||||
// WarmUp Ensure authentik's flow cache is warmed up
|
||||
func (fe *FlowExecutor) WarmUp() error {
|
||||
gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.get_challenge")
|
||||
|
@ -1,9 +1,14 @@
|
||||
package bind
|
||||
|
||||
import "github.com/nmcclain/ldap"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
type Binder interface {
|
||||
GetUsername(string) (string, error)
|
||||
Bind(username string, req *Request) (ldap.LDAPResultCode, error)
|
||||
TimerFlowCacheExpiry()
|
||||
Unbind(username string, req *Request) (ldap.LDAPResultCode, error)
|
||||
TimerFlowCacheExpiry(context.Context)
|
||||
}
|
||||
|
98
internal/outpost/ldap/bind/direct/bind.go
Normal file
98
internal/outpost/ldap/bind/direct/bind.go
Normal file
@ -0,0 +1,98 @@
|
||||
package direct
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/nmcclain/ldap"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/outpost/flow"
|
||||
"goauthentik.io/internal/outpost/ldap/bind"
|
||||
"goauthentik.io/internal/outpost/ldap/flags"
|
||||
"goauthentik.io/internal/outpost/ldap/metrics"
|
||||
)
|
||||
|
||||
func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResultCode, error) {
|
||||
fe := flow.NewFlowExecutor(req.Context(), db.si.GetAuthenticationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{
|
||||
"bindDN": req.BindDN,
|
||||
"client": req.RemoteAddr(),
|
||||
"requestId": req.ID(),
|
||||
})
|
||||
fe.DelegateClientIP(req.RemoteAddr())
|
||||
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
|
||||
fe.Answers[flow.StageIdentification] = username
|
||||
fe.Answers[flow.StagePassword] = req.BindPW
|
||||
|
||||
passed, err := fe.Execute()
|
||||
flags := flags.UserFlags{
|
||||
Session: fe.GetSession(),
|
||||
}
|
||||
db.si.SetFlags(req.BindDN, &flags)
|
||||
if err != nil {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "flow_error",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to execute flow")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
if !passed {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "invalid_credentials",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().Info("Invalid credentials")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
access, err := fe.CheckApplicationAccess(db.si.GetAppSlug())
|
||||
if !access {
|
||||
req.Log().Info("Access denied for user")
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "access_denied",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
if err != nil {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "access_check_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to check access")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
req.Log().Info("User has access")
|
||||
uisp := sentry.StartSpan(req.Context(), "authentik.providers.ldap.bind.user_info")
|
||||
// Get user info to store in context
|
||||
userInfo, _, err := fe.ApiClient().CoreApi.CoreUsersMeRetrieve(context.Background()).Execute()
|
||||
if err != nil {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "user_info_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to get user info")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
cs := db.SearchAccessCheck(userInfo.User)
|
||||
flags.UserPk = userInfo.User.Pk
|
||||
flags.CanSearch = cs != nil
|
||||
db.si.SetFlags(req.BindDN, &flags)
|
||||
if flags.CanSearch {
|
||||
req.Log().WithField("group", cs).Info("Allowed access to search")
|
||||
}
|
||||
uisp.Finish()
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
@ -5,16 +5,10 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/nmcclain/ldap"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api/v3"
|
||||
"goauthentik.io/internal/outpost/flow"
|
||||
"goauthentik.io/internal/outpost/ldap/bind"
|
||||
"goauthentik.io/internal/outpost/ldap/flags"
|
||||
"goauthentik.io/internal/outpost/ldap/metrics"
|
||||
"goauthentik.io/internal/outpost/ldap/server"
|
||||
"goauthentik.io/internal/outpost/ldap/utils"
|
||||
)
|
||||
@ -53,90 +47,6 @@ func (db *DirectBinder) GetUsername(dn string) (string, error) {
|
||||
return "", errors.New("failed to find cn")
|
||||
}
|
||||
|
||||
func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResultCode, error) {
|
||||
fe := flow.NewFlowExecutor(req.Context(), db.si.GetFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{
|
||||
"bindDN": req.BindDN,
|
||||
"client": req.RemoteAddr(),
|
||||
"requestId": req.ID(),
|
||||
})
|
||||
fe.DelegateClientIP(req.RemoteAddr())
|
||||
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
|
||||
fe.Answers[flow.StageIdentification] = username
|
||||
fe.Answers[flow.StagePassword] = req.BindPW
|
||||
|
||||
passed, err := fe.Execute()
|
||||
flags := flags.UserFlags{
|
||||
Session: fe.GetSession(),
|
||||
}
|
||||
db.si.SetFlags(req.BindDN, flags)
|
||||
if err != nil {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "flow_error",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to execute flow")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
if !passed {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "invalid_credentials",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().Info("Invalid credentials")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
access, err := fe.CheckApplicationAccess(db.si.GetAppSlug())
|
||||
if !access {
|
||||
req.Log().Info("Access denied for user")
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "access_denied",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
if err != nil {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "access_check_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to check access")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
req.Log().Info("User has access")
|
||||
uisp := sentry.StartSpan(req.Context(), "authentik.providers.ldap.bind.user_info")
|
||||
// Get user info to store in context
|
||||
userInfo, _, err := fe.ApiClient().CoreApi.CoreUsersMeRetrieve(context.Background()).Execute()
|
||||
if err != nil {
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
"type": "bind",
|
||||
"reason": "user_info_fail",
|
||||
"app": db.si.GetAppSlug(),
|
||||
}).Inc()
|
||||
req.Log().WithError(err).Warning("failed to get user info")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
cs := db.SearchAccessCheck(userInfo.User)
|
||||
flags.UserPk = userInfo.User.Pk
|
||||
flags.CanSearch = cs != nil
|
||||
db.si.SetFlags(req.BindDN, flags)
|
||||
if flags.CanSearch {
|
||||
req.Log().WithField("group", cs).Info("Allowed access to search")
|
||||
}
|
||||
uisp.Finish()
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
|
||||
// SearchAccessCheck Check if the current user is allowed to search
|
||||
func (db *DirectBinder) SearchAccessCheck(user api.UserSelf) *string {
|
||||
for _, group := range user.Groups {
|
||||
@ -153,8 +63,8 @@ func (db *DirectBinder) SearchAccessCheck(user api.UserSelf) *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DirectBinder) TimerFlowCacheExpiry() {
|
||||
fe := flow.NewFlowExecutor(context.Background(), db.si.GetFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{})
|
||||
func (db *DirectBinder) TimerFlowCacheExpiry(ctx context.Context) {
|
||||
fe := flow.NewFlowExecutor(ctx, db.si.GetAuthenticationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{})
|
||||
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
fe.Params.Add("goauthentik.io/outpost/ldap-warmup", "true")
|
||||
|
||||
|
29
internal/outpost/ldap/bind/direct/unbind.go
Normal file
29
internal/outpost/ldap/bind/direct/unbind.go
Normal file
@ -0,0 +1,29 @@
|
||||
package direct
|
||||
|
||||
import (
|
||||
"github.com/nmcclain/ldap"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/outpost/flow"
|
||||
"goauthentik.io/internal/outpost/ldap/bind"
|
||||
)
|
||||
|
||||
func (db *DirectBinder) Unbind(username string, req *bind.Request) (ldap.LDAPResultCode, error) {
|
||||
flags := db.si.GetFlags(req.BindDN)
|
||||
if flags == nil || flags.Session == nil {
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
fe := flow.NewFlowExecutor(req.Context(), db.si.GetInvalidationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{
|
||||
"boundDN": req.BindDN,
|
||||
"client": req.RemoteAddr(),
|
||||
"requestId": req.ID(),
|
||||
})
|
||||
fe.SetSession(flags.Session)
|
||||
fe.DelegateClientIP(req.RemoteAddr())
|
||||
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
_, err := fe.Execute()
|
||||
if err != nil {
|
||||
db.log.WithError(err).Warning("failed to logout user")
|
||||
}
|
||||
db.si.SetFlags(req.BindDN, nil)
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
@ -27,10 +27,11 @@ type ProviderInstance struct {
|
||||
searcher search.Searcher
|
||||
binder bind.Binder
|
||||
|
||||
appSlug string
|
||||
flowSlug string
|
||||
s *LDAPServer
|
||||
log *log.Entry
|
||||
appSlug string
|
||||
authenticationFlowSlug string
|
||||
invalidationFlowSlug string
|
||||
s *LDAPServer
|
||||
log *log.Entry
|
||||
|
||||
tlsServerName *string
|
||||
cert *tls.Certificate
|
||||
@ -79,9 +80,13 @@ func (pi *ProviderInstance) GetFlags(dn string) *flags.UserFlags {
|
||||
return flags
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) SetFlags(dn string, flag flags.UserFlags) {
|
||||
func (pi *ProviderInstance) SetFlags(dn string, flag *flags.UserFlags) {
|
||||
pi.boundUsersMutex.Lock()
|
||||
pi.boundUsers[dn] = &flag
|
||||
if flag == nil {
|
||||
delete(pi.boundUsers, dn)
|
||||
} else {
|
||||
pi.boundUsers[dn] = flag
|
||||
}
|
||||
pi.boundUsersMutex.Unlock()
|
||||
}
|
||||
|
||||
@ -89,8 +94,12 @@ func (pi *ProviderInstance) GetAppSlug() string {
|
||||
return pi.appSlug
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) GetFlowSlug() string {
|
||||
return pi.flowSlug
|
||||
func (pi *ProviderInstance) GetAuthenticationFlowSlug() string {
|
||||
return pi.authenticationFlowSlug
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) GetInvalidationFlowSlug() string {
|
||||
return pi.invalidationFlowSlug
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) GetSearchAllowedGroups() []*strfmt.UUID {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"sync"
|
||||
@ -40,6 +41,7 @@ func NewServer(ac *ak.APIController) *LDAPServer {
|
||||
}
|
||||
ls.defaultCert = &defaultCert
|
||||
s.BindFunc("", ls)
|
||||
s.UnbindFunc("", ls)
|
||||
s.SearchFunc("", ls)
|
||||
return ls
|
||||
}
|
||||
@ -92,9 +94,13 @@ func (ls *LDAPServer) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ls *LDAPServer) TimerFlowCacheExpiry() {
|
||||
func (ls *LDAPServer) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ls *LDAPServer) TimerFlowCacheExpiry(ctx context.Context) {
|
||||
for _, p := range ls.providers {
|
||||
ls.log.WithField("flow", p.flowSlug).Debug("Pre-heating flow cache")
|
||||
p.binder.TimerFlowCacheExpiry()
|
||||
ls.log.WithField("flow", p.authenticationFlowSlug).Debug("Pre-heating flow cache")
|
||||
p.binder.TimerFlowCacheExpiry(ctx)
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,16 @@ func (ls *LDAPServer) getCurrentProvider(pk int32) *ProviderInstance {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ls *LDAPServer) getInvalidationFlow() string {
|
||||
req, _, err := ls.ac.Client.CoreApi.CoreTenantsCurrentRetrieve(context.Background()).Execute()
|
||||
if err != nil {
|
||||
ls.log.WithError(err).Warning("failed to fetch tenant config")
|
||||
return ""
|
||||
}
|
||||
flow := req.GetFlowInvalidation()
|
||||
return flow
|
||||
}
|
||||
|
||||
func (ls *LDAPServer) Refresh() error {
|
||||
outposts, _, err := ls.ac.Client.OutpostsApi.OutpostsLdapList(context.Background()).Execute()
|
||||
if err != nil {
|
||||
@ -37,6 +47,7 @@ func (ls *LDAPServer) Refresh() error {
|
||||
return errors.New("no ldap provider defined")
|
||||
}
|
||||
providers := make([]*ProviderInstance, len(outposts.Results))
|
||||
invalidationFlow := ls.getInvalidationFlow()
|
||||
for idx, provider := range outposts.Results {
|
||||
userDN := strings.ToLower(fmt.Sprintf("ou=%s,%s", constants.OUUsers, *provider.BaseDn))
|
||||
groupDN := strings.ToLower(fmt.Sprintf("ou=%s,%s", constants.OUGroups, *provider.BaseDn))
|
||||
@ -53,22 +64,23 @@ func (ls *LDAPServer) Refresh() error {
|
||||
}
|
||||
|
||||
providers[idx] = &ProviderInstance{
|
||||
BaseDN: *provider.BaseDn,
|
||||
VirtualGroupDN: virtualGroupDN,
|
||||
GroupDN: groupDN,
|
||||
UserDN: userDN,
|
||||
appSlug: provider.ApplicationSlug,
|
||||
flowSlug: provider.BindFlowSlug,
|
||||
searchAllowedGroups: []*strfmt.UUID{(*strfmt.UUID)(provider.SearchGroup.Get())},
|
||||
boundUsersMutex: sync.RWMutex{},
|
||||
boundUsers: users,
|
||||
s: ls,
|
||||
log: logger,
|
||||
tlsServerName: provider.TlsServerName,
|
||||
uidStartNumber: *provider.UidStartNumber,
|
||||
gidStartNumber: *provider.GidStartNumber,
|
||||
outpostName: ls.ac.Outpost.Name,
|
||||
outpostPk: provider.Pk,
|
||||
BaseDN: *provider.BaseDn,
|
||||
VirtualGroupDN: virtualGroupDN,
|
||||
GroupDN: groupDN,
|
||||
UserDN: userDN,
|
||||
appSlug: provider.ApplicationSlug,
|
||||
authenticationFlowSlug: provider.BindFlowSlug,
|
||||
invalidationFlowSlug: invalidationFlow,
|
||||
searchAllowedGroups: []*strfmt.UUID{(*strfmt.UUID)(provider.SearchGroup.Get())},
|
||||
boundUsersMutex: sync.RWMutex{},
|
||||
boundUsers: users,
|
||||
s: ls,
|
||||
log: logger,
|
||||
tlsServerName: provider.TlsServerName,
|
||||
uidStartNumber: *provider.UidStartNumber,
|
||||
gidStartNumber: *provider.GidStartNumber,
|
||||
outpostName: ls.ac.Outpost.Name,
|
||||
outpostPk: provider.Pk,
|
||||
}
|
||||
if kp := provider.Certificate.Get(); kp != nil {
|
||||
err := ls.cs.AddKeypair(*kp)
|
||||
|
@ -11,7 +11,8 @@ type LDAPServerInstance interface {
|
||||
GetAPIClient() *api.APIClient
|
||||
GetOutpostName() string
|
||||
|
||||
GetFlowSlug() string
|
||||
GetAuthenticationFlowSlug() string
|
||||
GetInvalidationFlowSlug() string
|
||||
GetAppSlug() string
|
||||
GetSearchAllowedGroups() []*strfmt.UUID
|
||||
|
||||
@ -32,7 +33,7 @@ type LDAPServerInstance interface {
|
||||
UsersForGroup(api.Group) []string
|
||||
|
||||
GetFlags(dn string) *flags.UserFlags
|
||||
SetFlags(dn string, flags flags.UserFlags)
|
||||
SetFlags(dn string, flags *flags.UserFlags)
|
||||
|
||||
GetBaseEntry() *ldap.Entry
|
||||
GetNeededObjects(int, string, string) (bool, bool)
|
||||
|
53
internal/outpost/ldap/unbind.go
Normal file
53
internal/outpost/ldap/unbind.go
Normal file
@ -0,0 +1,53 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/nmcclain/ldap"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/outpost/ldap/bind"
|
||||
"goauthentik.io/internal/outpost/ldap/metrics"
|
||||
)
|
||||
|
||||
func (ls *LDAPServer) Unbind(boundDN string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
req, span := bind.NewRequest(boundDN, "", conn)
|
||||
selectedApp := ""
|
||||
defer func() {
|
||||
span.Finish()
|
||||
metrics.Requests.With(prometheus.Labels{
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "unbind",
|
||||
"app": selectedApp,
|
||||
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
|
||||
req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Unbind request")
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.WithError(err.(error)).Error("recover in bind request")
|
||||
sentry.CaptureException(err.(error))
|
||||
}()
|
||||
|
||||
for _, instance := range ls.providers {
|
||||
username, err := instance.binder.GetUsername(boundDN)
|
||||
if err == nil {
|
||||
selectedApp = instance.GetAppSlug()
|
||||
return instance.binder.Unbind(username, req)
|
||||
} else {
|
||||
req.Log().WithError(err).Debug("Username not for instance")
|
||||
}
|
||||
}
|
||||
req.Log().WithField("request", "unbind").Warning("No provider found for request")
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": ls.ac.Outpost.Name,
|
||||
"type": "unbind",
|
||||
"reason": "no_provider",
|
||||
"app": "",
|
||||
}).Inc()
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
@ -81,7 +81,7 @@ func (ps *ProxyServer) Type() string {
|
||||
return "proxy"
|
||||
}
|
||||
|
||||
func (ps *ProxyServer) TimerFlowCacheExpiry() {}
|
||||
func (ps *ProxyServer) TimerFlowCacheExpiry(context.Context) {}
|
||||
|
||||
func (ps *ProxyServer) GetCertificate(serverName string) *tls.Certificate {
|
||||
app, ok := ps.apps[serverName]
|
||||
@ -163,6 +163,10 @@ func (ps *ProxyServer) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *ProxyServer) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *ProxyServer) serve(listener net.Listener) {
|
||||
srv := &http.Server{Handler: ps.mux}
|
||||
|
||||
|
@ -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
120
poetry.lock
generated
@ -789,63 +789,63 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.0.5"
|
||||
version = "7.1.0"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"},
|
||||
{file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"},
|
||||
{file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"},
|
||||
{file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"},
|
||||
{file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"},
|
||||
{file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"},
|
||||
{file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"},
|
||||
{file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-win32.whl", hash = "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352"},
|
||||
{file = "coverage-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-win32.whl", hash = "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7"},
|
||||
{file = "coverage-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73"},
|
||||
{file = "coverage-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-win32.whl", hash = "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab"},
|
||||
{file = "coverage-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-win32.whl", hash = "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4"},
|
||||
{file = "coverage-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3"},
|
||||
{file = "coverage-7.1.0-pp37.pp38.pp39-none-any.whl", hash = "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda"},
|
||||
{file = "coverage-7.1.0.tar.gz", hash = "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -2851,14 +2851,14 @@ pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "selenium"
|
||||
version = "4.7.2"
|
||||
version = "4.8.0"
|
||||
description = ""
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "~=3.7"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "selenium-4.7.2-py3-none-any.whl", hash = "sha256:06a1c7d9f313130b21c3218ddd8852070d0e7419afdd31f96160cd576555a5ce"},
|
||||
{file = "selenium-4.7.2.tar.gz", hash = "sha256:3aefa14a28a42e520550c1cd0f29cf1d566328186ea63aa9a3e01fb265b5894d"},
|
||||
{file = "selenium-4.8.0-py3-none-any.whl", hash = "sha256:20f28ee4ea9b273b4112a7df5276ebb3052f79ff6eff42a564db6143e5926683"},
|
||||
{file = "selenium-4.8.0.tar.gz", hash = "sha256:fee36724d6cf0b18c73781bb8ec7be4a35ab1e2564e64e64e64da75e50e052af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2869,14 +2869,14 @@ urllib3 = {version = ">=1.26,<2.0", extras = ["socks"]}
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "1.13.0"
|
||||
version = "1.14.0"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "sentry-sdk-1.13.0.tar.gz", hash = "sha256:72da0766c3069a3941eadbdfa0996f83f5a33e55902a19ba399557cfee1dddcc"},
|
||||
{file = "sentry_sdk-1.13.0-py2.py3-none-any.whl", hash = "sha256:b7ff6318183e551145b5c4766eb65b59ad5b63ff234dffddc5fb50340cad6729"},
|
||||
{file = "sentry-sdk-1.14.0.tar.gz", hash = "sha256:273fe05adf052b40fd19f6d4b9a5556316807246bd817e5e3482930730726bb0"},
|
||||
{file = "sentry_sdk-1.14.0-py2.py3-none-any.whl", hash = "sha256:72c00322217d813cf493fe76590b23a757e063ff62fec59299f4af7201dd4448"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2894,7 +2894,7 @@ falcon = ["falcon (>=1.4)"]
|
||||
fastapi = ["fastapi (>=0.79.0)"]
|
||||
flask = ["blinker (>=1.1)", "flask (>=0.11)"]
|
||||
httpx = ["httpx (>=0.16.0)"]
|
||||
opentelemetry = ["opentelemetry-distro (>=0.350b0)"]
|
||||
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||
pymongo = ["pymongo (>=3.1)"]
|
||||
pyspark = ["pyspark (>=2.4.4)"]
|
||||
|
14
schema.yml
14
schema.yml
@ -23008,6 +23008,10 @@ paths:
|
||||
name: label
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
@ -34263,6 +34267,9 @@ components:
|
||||
type: object
|
||||
description: Prompt Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
field_key:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -35278,6 +35285,8 @@ components:
|
||||
format: uuid
|
||||
readOnly: true
|
||||
title: Prompt uuid
|
||||
name:
|
||||
type: string
|
||||
field_key:
|
||||
type: string
|
||||
description: Name of the form field, also used to store the value
|
||||
@ -35304,6 +35313,7 @@ components:
|
||||
required:
|
||||
- field_key
|
||||
- label
|
||||
- name
|
||||
- pk
|
||||
- type
|
||||
PromptChallenge:
|
||||
@ -35345,6 +35355,9 @@ components:
|
||||
type: object
|
||||
description: Prompt Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
field_key:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -35373,6 +35386,7 @@ components:
|
||||
required:
|
||||
- field_key
|
||||
- label
|
||||
- name
|
||||
- type
|
||||
PromptStage:
|
||||
type: object
|
||||
|
@ -26,8 +26,8 @@ class TestFlowsAuthenticator(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_totp_validate(self):
|
||||
"""test flow with otp stages"""
|
||||
@ -52,10 +52,10 @@ class TestFlowsAuthenticator(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint("default/20-flow-default-authenticator-totp-setup.yaml")
|
||||
@apply_blueprint("default/flow-default-authenticator-totp-setup.yaml")
|
||||
def test_totp_setup(self):
|
||||
"""test TOTP Setup stage"""
|
||||
flow: Flow = Flow.objects.get(slug="default-authentication-flow")
|
||||
@ -98,10 +98,10 @@ class TestFlowsAuthenticator(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint("default/20-flow-default-authenticator-static-setup.yaml")
|
||||
@apply_blueprint("default/flow-default-authenticator-static-setup.yaml")
|
||||
def test_static_setup(self):
|
||||
"""test Static OTP Setup stage"""
|
||||
flow: Flow = Flow.objects.get(slug="default-authentication-flow")
|
||||
|
@ -41,19 +41,28 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_enroll_2_step(self):
|
||||
"""Test 2-step enroll flow"""
|
||||
# First stage fields
|
||||
username_prompt = Prompt.objects.create(
|
||||
field_key="username", label="Username", order=0, type=FieldTypes.TEXT
|
||||
name=generate_id(),
|
||||
field_key="username",
|
||||
label="Username",
|
||||
order=0,
|
||||
type=FieldTypes.TEXT,
|
||||
)
|
||||
password = Prompt.objects.create(
|
||||
field_key="password", label="Password", order=1, type=FieldTypes.PASSWORD
|
||||
name=generate_id(),
|
||||
field_key="password",
|
||||
label="Password",
|
||||
order=1,
|
||||
type=FieldTypes.PASSWORD,
|
||||
)
|
||||
password_repeat = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="password_repeat",
|
||||
label="Password (repeat)",
|
||||
order=2,
|
||||
@ -62,10 +71,10 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
|
||||
# Second stage fields
|
||||
name_field = Prompt.objects.create(
|
||||
field_key="name", label="Name", order=0, type=FieldTypes.TEXT
|
||||
name=generate_id(), field_key="name", label="Name", order=0, type=FieldTypes.TEXT
|
||||
)
|
||||
email = Prompt.objects.create(
|
||||
field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
|
||||
name=generate_id(), field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
|
||||
)
|
||||
|
||||
# Stages
|
||||
@ -107,19 +116,28 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_enroll_email(self):
|
||||
"""Test enroll with Email verification"""
|
||||
# First stage fields
|
||||
username_prompt = Prompt.objects.create(
|
||||
field_key="username", label="Username", order=0, type=FieldTypes.TEXT
|
||||
name=generate_id(),
|
||||
field_key="username",
|
||||
label="Username",
|
||||
order=0,
|
||||
type=FieldTypes.TEXT,
|
||||
)
|
||||
password = Prompt.objects.create(
|
||||
field_key="password", label="Password", order=1, type=FieldTypes.PASSWORD
|
||||
name=generate_id(),
|
||||
field_key="password",
|
||||
label="Password",
|
||||
order=1,
|
||||
type=FieldTypes.PASSWORD,
|
||||
)
|
||||
password_repeat = Prompt.objects.create(
|
||||
name=generate_id(),
|
||||
field_key="password_repeat",
|
||||
label="Password (repeat)",
|
||||
order=2,
|
||||
@ -128,10 +146,10 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||
|
||||
# Second stage fields
|
||||
name_field = Prompt.objects.create(
|
||||
field_key="name", label="Name", order=0, type=FieldTypes.TEXT
|
||||
name=generate_id(), field_key="name", label="Name", order=0, type=FieldTypes.TEXT
|
||||
)
|
||||
email = Prompt.objects.create(
|
||||
field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
|
||||
name=generate_id(), field_key="email", label="E-Mail", order=1, type=FieldTypes.EMAIL
|
||||
)
|
||||
|
||||
# Stages
|
||||
|
@ -12,8 +12,8 @@ class TestFlowsLogin(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_login(self):
|
||||
"""test default login flow"""
|
||||
|
@ -18,10 +18,10 @@ class TestFlowsStageSetup(SeleniumTestCase):
|
||||
"""test stage setup flows"""
|
||||
|
||||
@retry()
|
||||
@apply_blueprint("default/0-flow-password-change.yaml")
|
||||
@apply_blueprint("default/flow-password-change.yaml")
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_password_change(self):
|
||||
"""test password change flow"""
|
||||
|
@ -81,8 +81,8 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_ldap_bind_success(self):
|
||||
"""Test simple bind"""
|
||||
@ -108,8 +108,8 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_ldap_bind_success_ssl(self):
|
||||
"""Test simple bind with ssl"""
|
||||
@ -135,8 +135,8 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
def test_ldap_bind_fail(self):
|
||||
"""Test simple bind (failed)"""
|
||||
@ -160,8 +160,8 @@ class TestProviderLDAP(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_outposts")
|
||||
def test_ldap_bind_search(self):
|
||||
|
@ -58,12 +58,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -114,12 +114,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -189,12 +189,12 @@ class TestProviderOAuth2Github(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_denied(self):
|
||||
|
@ -67,12 +67,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -116,12 +116,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -178,12 +178,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -249,12 +249,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -329,12 +329,12 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
|
@ -65,12 +65,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_redirect_uri_error(self):
|
||||
@ -111,12 +111,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@ -166,12 +166,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@ -236,12 +236,12 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_denied(self):
|
||||
|
@ -65,12 +65,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_redirect_uri_error(self):
|
||||
@ -111,12 +111,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@ -161,12 +161,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
@ -227,12 +227,12 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_authorization_denied(self):
|
||||
|
@ -57,12 +57,12 @@ class TestProviderProxy(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -123,12 +123,12 @@ class TestProviderProxy(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-oauth2.yaml",
|
||||
@ -200,12 +200,12 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@reconcile_app("authentik_crypto")
|
||||
def test_proxy_connectivity(self):
|
||||
|
@ -64,12 +64,12 @@ class TestProviderSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-saml.yaml",
|
||||
@ -133,12 +133,12 @@ class TestProviderSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-saml.yaml",
|
||||
@ -217,12 +217,12 @@ class TestProviderSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-saml.yaml",
|
||||
@ -301,12 +301,12 @@ class TestProviderSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-saml.yaml",
|
||||
@ -376,12 +376,12 @@ class TestProviderSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-saml.yaml",
|
||||
@ -425,12 +425,12 @@ class TestProviderSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"system/providers-saml.yaml",
|
||||
|
@ -143,17 +143,17 @@ class TestSourceOAuth2(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-source-authentication.yaml",
|
||||
"default/20-flow-default-source-enrollment.yaml",
|
||||
"default/20-flow-default-source-pre-authentication.yaml",
|
||||
"default/flow-default-source-authentication.yaml",
|
||||
"default/flow-default-source-enrollment.yaml",
|
||||
"default/flow-default-source-pre-authentication.yaml",
|
||||
)
|
||||
def test_oauth_enroll(self):
|
||||
"""test OAuth Source With With OIDC"""
|
||||
@ -200,12 +200,12 @@ class TestSourceOAuth2(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/20-flow-default-provider-authorization-implicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-explicit-consent.yaml",
|
||||
"default/flow-default-provider-authorization-implicit-consent.yaml",
|
||||
)
|
||||
def test_oauth_enroll_auth(self):
|
||||
"""test OAuth Source With With OIDC (enroll and authenticate again)"""
|
||||
@ -292,13 +292,13 @@ class TestSourceOAuth1(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-source-authentication.yaml",
|
||||
"default/20-flow-default-source-enrollment.yaml",
|
||||
"default/20-flow-default-source-pre-authentication.yaml",
|
||||
"default/flow-default-source-authentication.yaml",
|
||||
"default/flow-default-source-enrollment.yaml",
|
||||
"default/flow-default-source-pre-authentication.yaml",
|
||||
)
|
||||
def test_oauth_enroll(self):
|
||||
"""test OAuth Source With With OIDC"""
|
||||
|
@ -96,13 +96,13 @@ class TestSourceSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-source-authentication.yaml",
|
||||
"default/20-flow-default-source-enrollment.yaml",
|
||||
"default/20-flow-default-source-pre-authentication.yaml",
|
||||
"default/flow-default-source-authentication.yaml",
|
||||
"default/flow-default-source-enrollment.yaml",
|
||||
"default/flow-default-source-pre-authentication.yaml",
|
||||
)
|
||||
def test_idp_redirect(self):
|
||||
"""test SAML Source With redirect binding"""
|
||||
@ -166,13 +166,13 @@ class TestSourceSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-source-authentication.yaml",
|
||||
"default/20-flow-default-source-enrollment.yaml",
|
||||
"default/20-flow-default-source-pre-authentication.yaml",
|
||||
"default/flow-default-source-authentication.yaml",
|
||||
"default/flow-default-source-enrollment.yaml",
|
||||
"default/flow-default-source-pre-authentication.yaml",
|
||||
)
|
||||
def test_idp_post(self):
|
||||
"""test SAML Source With post binding"""
|
||||
@ -249,13 +249,13 @@ class TestSourceSAML(SeleniumTestCase):
|
||||
|
||||
@retry()
|
||||
@apply_blueprint(
|
||||
"default/10-flow-default-authentication-flow.yaml",
|
||||
"default/10-flow-default-invalidation-flow.yaml",
|
||||
"default/flow-default-authentication-flow.yaml",
|
||||
"default/flow-default-invalidation-flow.yaml",
|
||||
)
|
||||
@apply_blueprint(
|
||||
"default/20-flow-default-source-authentication.yaml",
|
||||
"default/20-flow-default-source-enrollment.yaml",
|
||||
"default/20-flow-default-source-pre-authentication.yaml",
|
||||
"default/flow-default-source-authentication.yaml",
|
||||
"default/flow-default-source-enrollment.yaml",
|
||||
"default/flow-default-source-pre-authentication.yaml",
|
||||
)
|
||||
def test_idp_post_auto(self):
|
||||
"""test SAML Source With post binding (auto redirect)"""
|
||||
|
432
web/package-lock.json
generated
432
web/package-lock.json
generated
@ -21,13 +21,13 @@
|
||||
"@codemirror/legacy-modes": "^6.3.1",
|
||||
"@formatjs/intl-listformat": "^7.1.7",
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@goauthentik/api": "^2023.1.0-1674058489",
|
||||
"@goauthentik/api": "^2023.1.2-1674559422",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||
"@lingui/cli": "^3.16.0",
|
||||
"@lingui/cli": "^3.16.1",
|
||||
"@lingui/core": "^3.16.0",
|
||||
"@lingui/detect-locale": "^3.16.0",
|
||||
"@lingui/macro": "^3.16.0",
|
||||
"@lingui/detect-locale": "^3.16.1",
|
||||
"@lingui/macro": "^3.16.1",
|
||||
"@patternfly/patternfly": "^4.222.4",
|
||||
"@polymer/iron-form": "^3.0.1",
|
||||
"@polymer/paper-input": "^3.2.1",
|
||||
@ -36,15 +36,15 @@
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@sentry/browser": "^7.31.1",
|
||||
"@sentry/tracing": "^7.31.1",
|
||||
"@sentry/browser": "^7.33.0",
|
||||
"@sentry/tracing": "^7.33.0",
|
||||
"@squoosh/cli": "^0.7.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
|
||||
"@types/chart.js": "^2.9.37",
|
||||
"@types/codemirror": "5.60.6",
|
||||
"@types/codemirror": "5.60.7",
|
||||
"@types/grecaptcha": "^3.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
||||
"@typescript-eslint/parser": "^5.48.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.49.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.7.0",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
@ -64,7 +64,7 @@
|
||||
"mermaid": "^9.3.0",
|
||||
"moment": "^2.29.4",
|
||||
"prettier": "^2.8.3",
|
||||
"pyright": "^1.1.290",
|
||||
"pyright": "^1.1.291",
|
||||
"rapidoc": "^9.3.4",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
@ -1961,9 +1961,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/api": {
|
||||
"version": "2023.1.0-1674058489",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.0-1674058489.tgz",
|
||||
"integrity": "sha512-k100Z1Tx4o7EsIq+tezavHSSddpr51NlGJZyR9QHsaQq6K+BpxZVcozR7xau7zK615JAwljWeG4yfEq9to++rw=="
|
||||
"version": "2023.1.2-1674559422",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.2-1674559422.tgz",
|
||||
"integrity": "sha512-zkbsIGy9J5QvtL70JH/svY0vGvbJhfYkhBxlt61dxCI3qDaof2RjJvm913OP6N/h8JoSzbyj8uZGJs4y8pHLhw=="
|
||||
},
|
||||
"node_modules/@hcaptcha/types": {
|
||||
"version": "1.0.3",
|
||||
@ -2215,13 +2215,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/babel-plugin-extract-messages": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.0.tgz",
|
||||
"integrity": "sha512-Wxfp9cJfVjwhVigufIssoDntaTm4a8XXkmsV9gTMCr2xJL6ZowoC80TltBLOu0Dt829e6pdy+TFuwuKwAvnmrA==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.1.tgz",
|
||||
"integrity": "sha512-ipt2WDGzSs0zsZ3BysFkDzz/LjUxSx5xh6/ScbXqcYDZc/b0jL87cj9S9csZdsoXLWWfeaPHvIgD5yTSthkM6w==",
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.11.6",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@lingui/conf": "3.16.0",
|
||||
"@lingui/conf": "3.16.1",
|
||||
"mkdirp": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -2229,18 +2229,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.0.tgz",
|
||||
"integrity": "sha512-6sUVpA6UB4BwNtLjC9aG62QvxkhHwVmDOi9vO8kb9W4SKPBaMdHuaxEsVVBuD1ZWcFZULcNUmPh9kKAnMRkHHw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.1.tgz",
|
||||
"integrity": "sha512-20IezHftBqJe8EYls+fwftni5f2dBWFe+d2J7a6DRKuvuSxBD2UforLPna9dq1Ya84MnZGQBhhY4zSXIXvO+fA==",
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.11.6",
|
||||
"@babel/parser": "^7.11.5",
|
||||
"@babel/plugin-syntax-jsx": "^7.10.4",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@babel/types": "^7.11.5",
|
||||
"@lingui/babel-plugin-extract-messages": "3.16.0",
|
||||
"@lingui/conf": "3.16.0",
|
||||
"@lingui/core": "3.16.0",
|
||||
"@lingui/babel-plugin-extract-messages": "3.16.1",
|
||||
"@lingui/conf": "3.16.1",
|
||||
"@lingui/core": "3.16.1",
|
||||
"babel-plugin-macros": "^3.0.1",
|
||||
"bcp-47": "^1.0.7",
|
||||
"chalk": "^4.1.0",
|
||||
@ -2342,9 +2342,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/conf": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.0.tgz",
|
||||
"integrity": "sha512-65X3TySGzeYjVNE3YZDpF6LTYxpUiSRSyTJjEGB7ZSPpuL6VDMEUGCDsnur2ix8D+I8w0ife6ks4HPQt6owZmw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.1.tgz",
|
||||
"integrity": "sha512-F8aYpjFXItmJLm09BxDzUZTsK0bB7nxLpMkUmiawVuuyB+66mBanOQ1nQEn8+SdJHCqJo9sI1ZSg+1uFequGzg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"chalk": "^4.1.0",
|
||||
@ -2422,9 +2422,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/core": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.0.tgz",
|
||||
"integrity": "sha512-2uZvxHv4IWF7xIRG1o4oXDFCrAhE0945Ses1eALmv/NqQ8BslXWWSq0Zf51qt+ZqQ3RfzCdl4kslZEqdGRe0gw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.1.tgz",
|
||||
"integrity": "sha512-F/oos3IquuA71eVUfVBFY4teq9QuE3enGV65AMb9VB0vUTxsy6HAOIo1A2aZ4wJ0ST8V7FhYshYRU6qXguRjMQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@messageformat/parser": "^5.0.0",
|
||||
@ -2435,20 +2435,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/detect-locale": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.0.tgz",
|
||||
"integrity": "sha512-7FQy1ccPnVe0/GNAzTmEDrsPHZV1/nSsK6fRKyuOFUkiItc1bVDmlpVfM4UXerERx6Nez+N99Z6Vjc6rg3LARw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.1.tgz",
|
||||
"integrity": "sha512-zDm9grmwPjQ5FXoIpkt0Fvq2bH+d01lI7zTmNFdTcM4IK4vsgAYqSY2h94W+7A3vyHEeaIULm5XtLBePSLJLcQ==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/macro": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.0.tgz",
|
||||
"integrity": "sha512-3HfP1Bqr4i60P3LoQq/ukhDvel4a5oBSMPRuYBUpwqdnKOAbZuN5vnZmu3TrlXntWTYOvrwa71D8SYGmvevelw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.1.tgz",
|
||||
"integrity": "sha512-CmR6u37Wzb+P4FGJJtbjY3SSX6vQb1IOj3Wcv9DYKvi1nepMvtvc5hcgCQKmqTdDi29fx6re5lmfG8YnthLkwA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@lingui/conf": "3.16.0",
|
||||
"@lingui/conf": "3.16.1",
|
||||
"ramda": "^0.27.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -2974,14 +2974,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.31.1.tgz",
|
||||
"integrity": "sha512-Rg9F61S1tz1Dv3iUyyGP26bxoi7WJAG2+f2fBbSmFuJ+JTH4Jvu2/F1bBig8Dz01ejzVhbNSUUCfoDhSvksIsQ==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.33.0.tgz",
|
||||
"integrity": "sha512-bvExBTyLb7cLWLkHh0gch2W/oSw08Yo8DgEc+KkikOnvWd/xoEWUsYNydYGzV+bL1jqcOErsZy0fVsbzTmh71g==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "7.31.1",
|
||||
"@sentry/replay": "7.31.1",
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1",
|
||||
"@sentry/core": "7.33.0",
|
||||
"@sentry/replay": "7.33.0",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2994,12 +2994,12 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.31.1.tgz",
|
||||
"integrity": "sha512-quaNU6z8jabmatBTDi28Wpff2yzfWIp/IU4bbi2QOtEiCNT+TQJXqlRTRMu9xLrX7YzyKCL5X2gbit/85lyWUg==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.33.0.tgz",
|
||||
"integrity": "sha512-mrSgUnXjxHVi0cVea1lv7gC/Y66ya2a3atCHaPEij/+l+3APg5d0Ixt1zMx5YllMiZKf6wpxlZ0uwXcqdAAw+w==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -3012,26 +3012,26 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/replay": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.31.1.tgz",
|
||||
"integrity": "sha512-sLArvwZn6IwA/bASctyhxN7LhdCXJvMmyTynRfmk7pzuNzBMc5CNlHeIsDpHrfQuH53IKicvl6cHnHyclu5DSA==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.33.0.tgz",
|
||||
"integrity": "sha512-m6xpSdjsNCCGxAkk5ikPFv/sQAfWtieMEXLdeDZE9jnroVozweHpsUhZYhqzTpxVC5SA3jPyTQ6Ods5gRvTBfA==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "7.31.1",
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1"
|
||||
"@sentry/core": "7.33.0",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/tracing": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.31.1.tgz",
|
||||
"integrity": "sha512-kW6vNwddp2Ycq2JfTzveUEIRF9YQwvl7L6BBoOZt9oVnYlsPipEeyU2Q277LatHldr8hDo2tbz/vz2BQjO5GSw==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.33.0.tgz",
|
||||
"integrity": "sha512-MtcKyW/QJgXGrHf5+205xnIIl7yIT99MzuTkuKzQwmnmy/siD3U0X8RoCaGLzj6kkSIu4m7vyQZoyd3J+5D8lw==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "7.31.1",
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1",
|
||||
"@sentry/core": "7.33.0",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -3044,19 +3044,19 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.31.1.tgz",
|
||||
"integrity": "sha512-1uzr2l0AxEnxUX/S0EdmXUQ15/kDsam8Nbdw4Gai8SU764XwQgA/TTjoewVP597CDI/AHKan67Y630/Ylmkx9w==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.33.0.tgz",
|
||||
"integrity": "sha512-5kkmYjtBWSbPxfYGiXdZFPS6xpFBNlXvDqeX4NpCFXz6/LiEDn6tZ61kuCSFb8MZlyqyCX5WsP3aiI2FJfpGIA==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.31.1.tgz",
|
||||
"integrity": "sha512-ZsIPq29aNdP9q3R7qIzJhZ9WW+4DzE9g5SfGwx3UjTIxoRRBfdUJUbf7S+LKEdvCkKbyoDt6FLt5MiSJV43xBA==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.33.0.tgz",
|
||||
"integrity": "sha512-msp02GV1gOfaN5FjKjWxI00rtbYLXEE5cTGldhs/Dt9KI63dDk1nwPDkSLhg6joqRItAq0thlBh6un717HdWbg==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/types": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -3256,9 +3256,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/codemirror": {
|
||||
"version": "5.60.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz",
|
||||
"integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==",
|
||||
"version": "5.60.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz",
|
||||
"integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==",
|
||||
"dependencies": {
|
||||
"@types/tern": "*"
|
||||
}
|
||||
@ -3404,13 +3404,13 @@
|
||||
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz",
|
||||
"integrity": "sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/type-utils": "5.48.2",
|
||||
"@typescript-eslint/utils": "5.48.2",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/type-utils": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
@ -3450,13 +3450,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
|
||||
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
|
||||
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -3476,12 +3476,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz",
|
||||
"integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2"
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@ -3492,12 +3492,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz",
|
||||
"integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"@typescript-eslint/utils": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
},
|
||||
@ -3518,9 +3518,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
|
||||
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
@ -3530,12 +3530,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
|
||||
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -3570,15 +3570,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz",
|
||||
"integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
@ -3609,11 +3609,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz",
|
||||
"integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -8170,9 +8170,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pyright": {
|
||||
"version": "1.1.290",
|
||||
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.290.tgz",
|
||||
"integrity": "sha512-iHT8G2+R/GUfYWxxd4ud5Lj/0H0bE1eWVR0avKKgPtkBeLkUz3sorjKpfuaJdsuJNrunzMtZsmYPD366t39klg==",
|
||||
"version": "1.1.291",
|
||||
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.291.tgz",
|
||||
"integrity": "sha512-wkSlCEhF1OwtJqHuifwxk76UMr1NPqsAbPIlCh7tiZx8VUdZs17NCGNorhyb2M+GFEvp8pod9Xyu1LXN1JL2kQ==",
|
||||
"bin": {
|
||||
"pyright": "index.js",
|
||||
"pyright-langserver": "langserver.index.js"
|
||||
@ -11437,9 +11437,9 @@
|
||||
"integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A=="
|
||||
},
|
||||
"@goauthentik/api": {
|
||||
"version": "2023.1.0-1674058489",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.0-1674058489.tgz",
|
||||
"integrity": "sha512-k100Z1Tx4o7EsIq+tezavHSSddpr51NlGJZyR9QHsaQq6K+BpxZVcozR7xau7zK615JAwljWeG4yfEq9to++rw=="
|
||||
"version": "2023.1.2-1674559422",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.1.2-1674559422.tgz",
|
||||
"integrity": "sha512-zkbsIGy9J5QvtL70JH/svY0vGvbJhfYkhBxlt61dxCI3qDaof2RjJvm913OP6N/h8JoSzbyj8uZGJs4y8pHLhw=="
|
||||
},
|
||||
"@hcaptcha/types": {
|
||||
"version": "1.0.3",
|
||||
@ -11650,29 +11650,29 @@
|
||||
}
|
||||
},
|
||||
"@lingui/babel-plugin-extract-messages": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.0.tgz",
|
||||
"integrity": "sha512-Wxfp9cJfVjwhVigufIssoDntaTm4a8XXkmsV9gTMCr2xJL6ZowoC80TltBLOu0Dt829e6pdy+TFuwuKwAvnmrA==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.16.1.tgz",
|
||||
"integrity": "sha512-ipt2WDGzSs0zsZ3BysFkDzz/LjUxSx5xh6/ScbXqcYDZc/b0jL87cj9S9csZdsoXLWWfeaPHvIgD5yTSthkM6w==",
|
||||
"requires": {
|
||||
"@babel/generator": "^7.11.6",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@lingui/conf": "3.16.0",
|
||||
"@lingui/conf": "3.16.1",
|
||||
"mkdirp": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"@lingui/cli": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.0.tgz",
|
||||
"integrity": "sha512-6sUVpA6UB4BwNtLjC9aG62QvxkhHwVmDOi9vO8kb9W4SKPBaMdHuaxEsVVBuD1ZWcFZULcNUmPh9kKAnMRkHHw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.16.1.tgz",
|
||||
"integrity": "sha512-20IezHftBqJe8EYls+fwftni5f2dBWFe+d2J7a6DRKuvuSxBD2UforLPna9dq1Ya84MnZGQBhhY4zSXIXvO+fA==",
|
||||
"requires": {
|
||||
"@babel/generator": "^7.11.6",
|
||||
"@babel/parser": "^7.11.5",
|
||||
"@babel/plugin-syntax-jsx": "^7.10.4",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@babel/types": "^7.11.5",
|
||||
"@lingui/babel-plugin-extract-messages": "3.16.0",
|
||||
"@lingui/conf": "3.16.0",
|
||||
"@lingui/core": "3.16.0",
|
||||
"@lingui/babel-plugin-extract-messages": "3.16.1",
|
||||
"@lingui/conf": "3.16.1",
|
||||
"@lingui/core": "3.16.1",
|
||||
"babel-plugin-macros": "^3.0.1",
|
||||
"bcp-47": "^1.0.7",
|
||||
"chalk": "^4.1.0",
|
||||
@ -11744,9 +11744,9 @@
|
||||
}
|
||||
},
|
||||
"@lingui/conf": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.0.tgz",
|
||||
"integrity": "sha512-65X3TySGzeYjVNE3YZDpF6LTYxpUiSRSyTJjEGB7ZSPpuL6VDMEUGCDsnur2ix8D+I8w0ife6ks4HPQt6owZmw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.16.1.tgz",
|
||||
"integrity": "sha512-F8aYpjFXItmJLm09BxDzUZTsK0bB7nxLpMkUmiawVuuyB+66mBanOQ1nQEn8+SdJHCqJo9sI1ZSg+1uFequGzg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"chalk": "^4.1.0",
|
||||
@ -11802,9 +11802,9 @@
|
||||
}
|
||||
},
|
||||
"@lingui/core": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.0.tgz",
|
||||
"integrity": "sha512-2uZvxHv4IWF7xIRG1o4oXDFCrAhE0945Ses1eALmv/NqQ8BslXWWSq0Zf51qt+ZqQ3RfzCdl4kslZEqdGRe0gw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.16.1.tgz",
|
||||
"integrity": "sha512-F/oos3IquuA71eVUfVBFY4teq9QuE3enGV65AMb9VB0vUTxsy6HAOIo1A2aZ4wJ0ST8V7FhYshYRU6qXguRjMQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@messageformat/parser": "^5.0.0",
|
||||
@ -11812,17 +11812,17 @@
|
||||
}
|
||||
},
|
||||
"@lingui/detect-locale": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.0.tgz",
|
||||
"integrity": "sha512-7FQy1ccPnVe0/GNAzTmEDrsPHZV1/nSsK6fRKyuOFUkiItc1bVDmlpVfM4UXerERx6Nez+N99Z6Vjc6rg3LARw=="
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-3.16.1.tgz",
|
||||
"integrity": "sha512-zDm9grmwPjQ5FXoIpkt0Fvq2bH+d01lI7zTmNFdTcM4IK4vsgAYqSY2h94W+7A3vyHEeaIULm5XtLBePSLJLcQ=="
|
||||
},
|
||||
"@lingui/macro": {
|
||||
"version": "3.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.0.tgz",
|
||||
"integrity": "sha512-3HfP1Bqr4i60P3LoQq/ukhDvel4a5oBSMPRuYBUpwqdnKOAbZuN5vnZmu3TrlXntWTYOvrwa71D8SYGmvevelw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.16.1.tgz",
|
||||
"integrity": "sha512-CmR6u37Wzb+P4FGJJtbjY3SSX6vQb1IOj3Wcv9DYKvi1nepMvtvc5hcgCQKmqTdDi29fx6re5lmfG8YnthLkwA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@lingui/conf": "3.16.0",
|
||||
"@lingui/conf": "3.16.1",
|
||||
"ramda": "^0.27.1"
|
||||
}
|
||||
},
|
||||
@ -12194,14 +12194,14 @@
|
||||
}
|
||||
},
|
||||
"@sentry/browser": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.31.1.tgz",
|
||||
"integrity": "sha512-Rg9F61S1tz1Dv3iUyyGP26bxoi7WJAG2+f2fBbSmFuJ+JTH4Jvu2/F1bBig8Dz01ejzVhbNSUUCfoDhSvksIsQ==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.33.0.tgz",
|
||||
"integrity": "sha512-bvExBTyLb7cLWLkHh0gch2W/oSw08Yo8DgEc+KkikOnvWd/xoEWUsYNydYGzV+bL1jqcOErsZy0fVsbzTmh71g==",
|
||||
"requires": {
|
||||
"@sentry/core": "7.31.1",
|
||||
"@sentry/replay": "7.31.1",
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1",
|
||||
"@sentry/core": "7.33.0",
|
||||
"@sentry/replay": "7.33.0",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -12213,12 +12213,12 @@
|
||||
}
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.31.1.tgz",
|
||||
"integrity": "sha512-quaNU6z8jabmatBTDi28Wpff2yzfWIp/IU4bbi2QOtEiCNT+TQJXqlRTRMu9xLrX7YzyKCL5X2gbit/85lyWUg==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.33.0.tgz",
|
||||
"integrity": "sha512-mrSgUnXjxHVi0cVea1lv7gC/Y66ya2a3atCHaPEij/+l+3APg5d0Ixt1zMx5YllMiZKf6wpxlZ0uwXcqdAAw+w==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -12230,23 +12230,23 @@
|
||||
}
|
||||
},
|
||||
"@sentry/replay": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.31.1.tgz",
|
||||
"integrity": "sha512-sLArvwZn6IwA/bASctyhxN7LhdCXJvMmyTynRfmk7pzuNzBMc5CNlHeIsDpHrfQuH53IKicvl6cHnHyclu5DSA==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.33.0.tgz",
|
||||
"integrity": "sha512-m6xpSdjsNCCGxAkk5ikPFv/sQAfWtieMEXLdeDZE9jnroVozweHpsUhZYhqzTpxVC5SA3jPyTQ6Ods5gRvTBfA==",
|
||||
"requires": {
|
||||
"@sentry/core": "7.31.1",
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1"
|
||||
"@sentry/core": "7.33.0",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0"
|
||||
}
|
||||
},
|
||||
"@sentry/tracing": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.31.1.tgz",
|
||||
"integrity": "sha512-kW6vNwddp2Ycq2JfTzveUEIRF9YQwvl7L6BBoOZt9oVnYlsPipEeyU2Q277LatHldr8hDo2tbz/vz2BQjO5GSw==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.33.0.tgz",
|
||||
"integrity": "sha512-MtcKyW/QJgXGrHf5+205xnIIl7yIT99MzuTkuKzQwmnmy/siD3U0X8RoCaGLzj6kkSIu4m7vyQZoyd3J+5D8lw==",
|
||||
"requires": {
|
||||
"@sentry/core": "7.31.1",
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/utils": "7.31.1",
|
||||
"@sentry/core": "7.33.0",
|
||||
"@sentry/types": "7.33.0",
|
||||
"@sentry/utils": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -12258,16 +12258,16 @@
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.31.1.tgz",
|
||||
"integrity": "sha512-1uzr2l0AxEnxUX/S0EdmXUQ15/kDsam8Nbdw4Gai8SU764XwQgA/TTjoewVP597CDI/AHKan67Y630/Ylmkx9w=="
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.33.0.tgz",
|
||||
"integrity": "sha512-5kkmYjtBWSbPxfYGiXdZFPS6xpFBNlXvDqeX4NpCFXz6/LiEDn6tZ61kuCSFb8MZlyqyCX5WsP3aiI2FJfpGIA=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "7.31.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.31.1.tgz",
|
||||
"integrity": "sha512-ZsIPq29aNdP9q3R7qIzJhZ9WW+4DzE9g5SfGwx3UjTIxoRRBfdUJUbf7S+LKEdvCkKbyoDt6FLt5MiSJV43xBA==",
|
||||
"version": "7.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.33.0.tgz",
|
||||
"integrity": "sha512-msp02GV1gOfaN5FjKjWxI00rtbYLXEE5cTGldhs/Dt9KI63dDk1nwPDkSLhg6joqRItAq0thlBh6un717HdWbg==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.31.1",
|
||||
"@sentry/types": "7.33.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -12430,9 +12430,9 @@
|
||||
}
|
||||
},
|
||||
"@types/codemirror": {
|
||||
"version": "5.60.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz",
|
||||
"integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==",
|
||||
"version": "5.60.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.7.tgz",
|
||||
"integrity": "sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==",
|
||||
"requires": {
|
||||
"@types/tern": "*"
|
||||
}
|
||||
@ -12577,13 +12577,13 @@
|
||||
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz",
|
||||
"integrity": "sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/type-utils": "5.48.2",
|
||||
"@typescript-eslint/utils": "5.48.2",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/type-utils": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
@ -12603,48 +12603,48 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
|
||||
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
|
||||
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"debug": "^4.3.4"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz",
|
||||
"integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2"
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz",
|
||||
"integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"@typescript-eslint/utils": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"@typescript-eslint/utils": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
|
||||
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA=="
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg=="
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
|
||||
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -12663,15 +12663,15 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz",
|
||||
"integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"@typescript-eslint/scope-manager": "5.49.0",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
@ -12688,11 +12688,11 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz",
|
||||
"integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==",
|
||||
"version": "5.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/types": "5.49.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -16055,9 +16055,9 @@
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"pyright": {
|
||||
"version": "1.1.290",
|
||||
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.290.tgz",
|
||||
"integrity": "sha512-iHT8G2+R/GUfYWxxd4ud5Lj/0H0bE1eWVR0avKKgPtkBeLkUz3sorjKpfuaJdsuJNrunzMtZsmYPD366t39klg=="
|
||||
"version": "1.1.291",
|
||||
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.291.tgz",
|
||||
"integrity": "sha512-wkSlCEhF1OwtJqHuifwxk76UMr1NPqsAbPIlCh7tiZx8VUdZs17NCGNorhyb2M+GFEvp8pod9Xyu1LXN1JL2kQ=="
|
||||
},
|
||||
"qrjs": {
|
||||
"version": "0.1.2",
|
||||
|
@ -65,13 +65,13 @@
|
||||
"@codemirror/legacy-modes": "^6.3.1",
|
||||
"@formatjs/intl-listformat": "^7.1.7",
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@goauthentik/api": "^2023.1.0-1674058489",
|
||||
"@goauthentik/api": "^2023.1.2-1674559422",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||
"@lingui/cli": "^3.16.0",
|
||||
"@lingui/cli": "^3.16.1",
|
||||
"@lingui/core": "^3.16.0",
|
||||
"@lingui/detect-locale": "^3.16.0",
|
||||
"@lingui/macro": "^3.16.0",
|
||||
"@lingui/detect-locale": "^3.16.1",
|
||||
"@lingui/macro": "^3.16.1",
|
||||
"@patternfly/patternfly": "^4.222.4",
|
||||
"@polymer/iron-form": "^3.0.1",
|
||||
"@polymer/paper-input": "^3.2.1",
|
||||
@ -80,15 +80,15 @@
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@sentry/browser": "^7.31.1",
|
||||
"@sentry/tracing": "^7.31.1",
|
||||
"@sentry/browser": "^7.33.0",
|
||||
"@sentry/tracing": "^7.33.0",
|
||||
"@squoosh/cli": "^0.7.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
|
||||
"@types/chart.js": "^2.9.37",
|
||||
"@types/codemirror": "5.60.6",
|
||||
"@types/codemirror": "5.60.7",
|
||||
"@types/grecaptcha": "^3.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
||||
"@typescript-eslint/parser": "^5.48.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
||||
"@typescript-eslint/parser": "^5.49.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.7.0",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
@ -108,7 +108,7 @@
|
||||
"mermaid": "^9.3.0",
|
||||
"moment": "^2.29.4",
|
||||
"prettier": "^2.8.3",
|
||||
"pyright": "^1.1.290",
|
||||
"pyright": "^1.1.291",
|
||||
"rapidoc": "^9.3.4",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
|
@ -13,9 +13,11 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||
|
||||
import { BlueprintInstance, BlueprintInstanceStatusEnum, ManagedApi } from "@goauthentik/api";
|
||||
|
||||
export function BlueprintStatus(blueprint?: BlueprintInstance): string {
|
||||
@ -32,6 +34,7 @@ export function BlueprintStatus(blueprint?: BlueprintInstance): string {
|
||||
}
|
||||
return t`Unknown`;
|
||||
}
|
||||
|
||||
@customElement("ak-blueprint-list")
|
||||
export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
searchEnabled(): boolean {
|
||||
@ -47,11 +50,16 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
return "pf-icon pf-icon-blueprint";
|
||||
}
|
||||
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFDescriptionList);
|
||||
}
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<BlueprintInstance>> {
|
||||
return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsList({
|
||||
ordering: this.order,
|
||||
@ -96,9 +104,34 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderExpanded(item: BlueprintInstance): TemplateResult {
|
||||
return html`<td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Path`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<pre>${item.path}</pre>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</td>`;
|
||||
}
|
||||
|
||||
row(item: BlueprintInstance): TemplateResult[] {
|
||||
let description = undefined;
|
||||
const descKey = "blueprints.goauthentik.io/description";
|
||||
if (Object.hasOwn(item.metadata.labels, descKey)) {
|
||||
description = item.metadata.labels[descKey];
|
||||
}
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`<div>${item.name}</div>
|
||||
${description ? html`<small>${description}</small>` : html``}`,
|
||||
html`${BlueprintStatus(item)}`,
|
||||
html`${item.lastApplied.toLocaleString()}`,
|
||||
html`<ak-label color=${item.enabled ? PFColor.Green : PFColor.Red}>
|
||||
|
@ -132,6 +132,17 @@ export class PromptForm extends ModelForm<Prompt, string> {
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Unique name of this field, used for selecting fields in prompt stages.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${t`Field Key`} ?required=${true} name="fieldKey">
|
||||
<input
|
||||
type="text"
|
||||
|
@ -1,7 +1,6 @@
|
||||
import "@goauthentik/admin/stages/prompt/PromptForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { truncate } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/buttons/ModalButton";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
@ -35,7 +34,7 @@ export class PromptListPage extends TablePage<Prompt> {
|
||||
checkbox = true;
|
||||
|
||||
@property()
|
||||
order = "order";
|
||||
order = "name";
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<Prompt>> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsList({
|
||||
@ -48,8 +47,8 @@ export class PromptListPage extends TablePage<Prompt> {
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(t`Name`, "name"),
|
||||
new TableColumn(t`Field`, "field_key"),
|
||||
new TableColumn(t`Label`, "label"),
|
||||
new TableColumn(t`Type`, "type"),
|
||||
new TableColumn(t`Order`, "order"),
|
||||
new TableColumn(t`Stages`),
|
||||
@ -81,8 +80,8 @@ export class PromptListPage extends TablePage<Prompt> {
|
||||
|
||||
row(item: Prompt): TemplateResult[] {
|
||||
return [
|
||||
html`${item.fieldKey}`,
|
||||
html`${truncate(item.label, 20)}`,
|
||||
html`${item.name}`,
|
||||
html`<code>${item.fieldKey}</code>`,
|
||||
html`${item.type}`,
|
||||
html`${item.order}`,
|
||||
html`${item.promptstageSet?.map((stage) => {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 344 KiB After Width: | Height: | Size: 1.2 MiB |
@ -27,7 +27,7 @@ import "@goauthentik/flow/stages/password/PasswordStage";
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html, render } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
|
||||
@ -44,6 +44,7 @@ import {
|
||||
CapabilitiesEnum,
|
||||
ChallengeChoices,
|
||||
ChallengeTypes,
|
||||
ContextualFlowInfo,
|
||||
CurrentTenant,
|
||||
FlowChallengeResponseRequest,
|
||||
FlowErrorChallenge,
|
||||
@ -95,9 +96,28 @@ export class FlowExecutor extends AKElement implements StageHost {
|
||||
@property({ attribute: false })
|
||||
tenant!: CurrentTenant;
|
||||
|
||||
@property({ attribute: false })
|
||||
@state()
|
||||
inspectorOpen: boolean;
|
||||
|
||||
_flowInfo?: ContextualFlowInfo;
|
||||
|
||||
@state()
|
||||
set flowInfo(value: ContextualFlowInfo | undefined) {
|
||||
this._flowInfo = value;
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
this.shadowRoot
|
||||
?.querySelectorAll<HTMLDivElement>(".pf-c-background-image")
|
||||
.forEach((bg) => {
|
||||
bg.style.setProperty("--ak-flow-background", `url('${value?.background}')`);
|
||||
});
|
||||
}
|
||||
|
||||
get flowInfo(): ContextualFlowInfo | undefined {
|
||||
return this._flowInfo;
|
||||
}
|
||||
|
||||
ws: WebsocketClient;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@ -168,14 +188,6 @@ export class FlowExecutor extends AKElement implements StageHost {
|
||||
tenant().then((tenant) => (this.tenant = tenant));
|
||||
}
|
||||
|
||||
setBackground(url: string): void {
|
||||
this.shadowRoot
|
||||
?.querySelectorAll<HTMLDivElement>(".pf-c-background-image")
|
||||
.forEach((bg) => {
|
||||
bg.style.setProperty("--ak-flow-background", `url('${url}')`);
|
||||
});
|
||||
}
|
||||
|
||||
submit(payload?: FlowChallengeResponseRequest): Promise<boolean> {
|
||||
if (!payload) return Promise.reject();
|
||||
if (!this.challenge) return Promise.reject();
|
||||
@ -198,6 +210,9 @@ export class FlowExecutor extends AKElement implements StageHost {
|
||||
);
|
||||
}
|
||||
this.challenge = data;
|
||||
if (this.challenge.flowInfo) {
|
||||
this.flowInfo = this.challenge.flowInfo;
|
||||
}
|
||||
if (this.challenge.responseErrors) {
|
||||
return false;
|
||||
}
|
||||
@ -231,9 +246,8 @@ export class FlowExecutor extends AKElement implements StageHost {
|
||||
);
|
||||
}
|
||||
this.challenge = challenge;
|
||||
// Only set background on first update, flow won't change throughout execution
|
||||
if (this.challenge?.flowInfo?.background) {
|
||||
this.setBackground(this.challenge.flowInfo.background);
|
||||
if (this.challenge.flowInfo) {
|
||||
this.flowInfo = this.challenge.flowInfo;
|
||||
}
|
||||
})
|
||||
.catch((e: Error | ResponseError) => {
|
||||
@ -531,13 +545,11 @@ export class FlowExecutor extends AKElement implements StageHost {
|
||||
>${t`Powered by authentik`}</a
|
||||
>
|
||||
</li>
|
||||
${this.challenge?.flowInfo?.background?.startsWith(
|
||||
"/static",
|
||||
)
|
||||
${this.flowInfo?.background?.startsWith("/static")
|
||||
? html`
|
||||
<li>
|
||||
<a
|
||||
href="https://unsplash.com/@chrishenryphoto"
|
||||
href="https://unsplash.com/@saishmenon"
|
||||
>${t`Background image`}</a
|
||||
>
|
||||
</li>
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 238 KiB |
Binary file not shown.
After Width: | Height: | Size: 161 KiB |
Binary file not shown.
After Width: | Height: | Size: 170 KiB |
151
website/blog/2023-01-24-saas-should-not-be-the-default/item.md
Normal file
151
website/blog/2023-01-24-saas-should-not-be-the-default/item.md
Normal file
@ -0,0 +1,151 @@
|
||||
---
|
||||
title: "SaaS should not be the default: Why data breaches signal return to self-hosting"
|
||||
description: More companies are realizing that SaaS isn’t, and shouldn’t 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 Okta’s vendors (Sitel, a contact center) in January. During Okta’s initial investigation, the company didn’t warn its customers about the attack nor about its potential damage.
|
||||
|
||||
“At that time,” Okta admitted later, “We didn’t 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 didn’t focus on the attack itself but on the company’s response. Okta had two months to talk about the attack before LAPSUS$ forced them to and it’s 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. It’s 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 don’t 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.
|
||||
|
||||
It’s hard, bordering on impossible, to claim that any given company suppressed news about a data breach. But we can infer it’s 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 can’t 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, it’s 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. Let’s 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 company’s 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 company’s 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 don’t always seem to impair companies.
|
||||
|
||||
You couldn’t infer, for example, when the infamous Experian data breach occurred looking at its stock price alone.
|
||||
|
||||

|
||||
|
||||
(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 it’s 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 needn’t 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 don’t 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 aren’t 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 company’s professional reputation. Even if a data breach doesn’t 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):
|
||||
|
||||

|
||||
|
||||
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 aren’t 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% – don’t do security evaluations because they rely on the consequences of the security agreements in the contract.
|
||||
|
||||
### What’s 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, you’re incentivized, in a way even the best SaaS vendor isn’t, 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 – didn’t we learn our lessons the first time? It’s 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 aren’t coming back, I’m 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 Bellotti’s words, on public clouds in much the same way they did on mainframes.
|
||||
|
||||
“Technology doesn’t 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
|
||||
|
||||
It’s 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.”
|
||||
|
||||

|
||||
|
||||
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, Dropbox’s decision “is starting to look more like a pioneer expedition.” Dropbox is able to save money and manage their resources more closely. Fulton argues there’s 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, Okta’s Chief Security Officer, concludes his [post](https://www.okta.com/blog/2022/03/oktas-investigation-of-the-january-2022-compromise/) explaining the company’s 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. I’m confident that we are moving in the right direction and this incident will only serve to strengthen our commitment to security.”
|
||||
|
||||
You don’t have to impugn Okta’s 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, can’t be as aligned with your company’s 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, won’t 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 isn’t, and shouldn’t be, the default.
|
@ -34,6 +34,8 @@ Any additional `.yaml` file in `/blueprints` will be discovered and automaticall
|
||||
|
||||
To disable existing blueprints, an empty file can be mounted over the existing blueprint.
|
||||
|
||||
File-based blueprints are automatically removed once they become unavailable, however none of the objects created by those blueprints afre affected by this.
|
||||
|
||||
## Storage - OCI
|
||||
|
||||
Blueprints can also be stored in remote [OCI](https://opencontainers.org/) compliant registries. This includes GitHub Container Registry, Docker hub and many other registries.
|
||||
|
@ -60,3 +60,7 @@ Used by authentik's packaged blueprints to keep globals up-to-date. Should only
|
||||
#### `blueprints.goauthentik.io/instantiate`:
|
||||
|
||||
Configure if this blueprint should automatically be instantiated (defaults to `"true"`). When set to `"false"`, blueprints are listed and available to be instantiated via API/Browser.
|
||||
|
||||
#### `blueprints.goauthentik.io/description`:
|
||||
|
||||
Optionally set a description, which can be seen in the web interface.
|
||||
|
28
website/package-lock.json
generated
28
website/package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"@docusaurus/preset-classic": "2.2.0",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"disqus-react": "^1.1.5",
|
||||
"postcss": "^8.4.21",
|
||||
"rapidoc": "^9.3.4",
|
||||
"react": "^17.0.2",
|
||||
@ -5485,6 +5486,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/disqus-react": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/disqus-react/-/disqus-react-1.1.5.tgz",
|
||||
"integrity": "sha512-9fdG5m6c3wJzlCDLaMheuUagMVj3s5qgUSXdekpCsvzYOKG21AiuOoqyDzA0oXrpPnYzgpnsvPYqZ+i0hJPGZw==",
|
||||
"peerDependencies": {
|
||||
"react": "^15.6.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^15.6.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dns-equal": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
|
||||
@ -11512,9 +11522,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "0.7.32",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
|
||||
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==",
|
||||
"version": "0.7.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
|
||||
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -16549,6 +16559,12 @@
|
||||
"path-type": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"disqus-react": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/disqus-react/-/disqus-react-1.1.5.tgz",
|
||||
"integrity": "sha512-9fdG5m6c3wJzlCDLaMheuUagMVj3s5qgUSXdekpCsvzYOKG21AiuOoqyDzA0oXrpPnYzgpnsvPYqZ+i0hJPGZw==",
|
||||
"requires": {}
|
||||
},
|
||||
"dns-equal": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
|
||||
@ -20873,9 +20889,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.32",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz",
|
||||
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw=="
|
||||
"version": "0.7.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
|
||||
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw=="
|
||||
},
|
||||
"unherit": {
|
||||
"version": "1.1.3",
|
||||
|
@ -19,6 +19,7 @@
|
||||
"@docusaurus/preset-classic": "2.2.0",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"disqus-react": "^1.1.5",
|
||||
"postcss": "^8.4.21",
|
||||
"rapidoc": "^9.3.4",
|
||||
"react": "^17.0.2",
|
||||
|
@ -187,8 +187,8 @@ function Comparison() {
|
||||
<td className="result passed">
|
||||
<Check></Check>
|
||||
</td>
|
||||
<td className="result failed">
|
||||
<X></X>
|
||||
<td className="result passed">
|
||||
<Check></Check>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -345,8 +345,8 @@ function Comparison() {
|
||||
<td className="result passed">
|
||||
<Check></Check>
|
||||
</td>
|
||||
<td className="result failed">
|
||||
<X></X>
|
||||
<td className="result passed">
|
||||
<Check></Check>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
76
website/src/theme/BlogPostPage/index.js
Normal file
76
website/src/theme/BlogPostPage/index.js
Normal file
@ -0,0 +1,76 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
HtmlClassNameProvider,
|
||||
ThemeClassNames,
|
||||
} from "@docusaurus/theme-common";
|
||||
import {
|
||||
BlogPostProvider,
|
||||
useBlogPost,
|
||||
} from "@docusaurus/theme-common/internal";
|
||||
import BlogLayout from "@theme/BlogLayout";
|
||||
import BlogPostItem from "@theme/BlogPostItem";
|
||||
import BlogPostPaginator from "@theme/BlogPostPaginator";
|
||||
import BlogPostPageMetadata from "@theme/BlogPostPage/Metadata";
|
||||
import TOC from "@theme/TOC";
|
||||
import { DiscussionEmbed } from "disqus-react";
|
||||
|
||||
function BlogPostPageContent({ sidebar, children }) {
|
||||
const { metadata, toc } = useBlogPost();
|
||||
const { nextItem, prevItem, frontMatter } = metadata;
|
||||
const {
|
||||
hide_table_of_contents: hideTableOfContents,
|
||||
toc_min_heading_level: tocMinHeadingLevel,
|
||||
toc_max_heading_level: tocMaxHeadingLevel,
|
||||
} = frontMatter;
|
||||
return (
|
||||
<BlogLayout
|
||||
sidebar={sidebar}
|
||||
toc={
|
||||
!hideTableOfContents && toc.length > 0 ? (
|
||||
<TOC
|
||||
toc={toc}
|
||||
minHeadingLevel={tocMinHeadingLevel}
|
||||
maxHeadingLevel={tocMaxHeadingLevel}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<BlogPostItem>{children}</BlogPostItem>
|
||||
|
||||
{(nextItem || prevItem) && (
|
||||
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
|
||||
)}
|
||||
</BlogLayout>
|
||||
);
|
||||
}
|
||||
export default function BlogPostPage(props) {
|
||||
const BlogPostContent = props.content;
|
||||
const title = props.content.frontMatter.title.substring(0, 200);
|
||||
const fmtId = title.replace(/^\//, "").replaceAll(/[\s\/]/gi, "-");
|
||||
const disqusId = fmtId == "" ? "main" : fmtId;
|
||||
return (
|
||||
<BlogPostProvider content={props.content} isBlogPostPage>
|
||||
<HtmlClassNameProvider
|
||||
className={clsx(
|
||||
ThemeClassNames.wrapper.blogPages,
|
||||
ThemeClassNames.page.blogPostPage
|
||||
)}
|
||||
>
|
||||
<BlogPostPageMetadata />
|
||||
<BlogPostPageContent sidebar={props.sidebar}>
|
||||
<BlogPostContent />
|
||||
|
||||
<DiscussionEmbed
|
||||
shortname="goauthentik-io"
|
||||
config={{
|
||||
url: "https://goauthentik.io" + props.route.path,
|
||||
identifier: disqusId,
|
||||
title: title,
|
||||
}}
|
||||
/>
|
||||
</BlogPostPageContent>
|
||||
</HtmlClassNameProvider>
|
||||
</BlogPostProvider>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user