Compare commits
10 Commits
providers/
...
web/cleanu
| Author | SHA1 | Date | |
|---|---|---|---|
| 27e9e892e7 | |||
| 70bf745c0c | |||
| 8b4e0361c4 | |||
| 22cb5b7379 | |||
| 2d0117d096 | |||
| 035bda4eac | |||
| 50906214e5 | |||
| e505f274b6 | |||
| fe52f44dca | |||
| 3146e5a50f |
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
@ -7,6 +7,8 @@ on:
|
|||||||
- main
|
- main
|
||||||
- next
|
- next
|
||||||
- version-*
|
- version-*
|
||||||
|
paths-ignore:
|
||||||
|
- website/**
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from authentik import __version__, get_build_hash
|
from authentik import __version__, get_build_hash
|
||||||
from authentik.admin.tasks import VERSION_CACHE_KEY, VERSION_NULL, update_latest_version
|
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
|
|
||||||
|
|
||||||
@ -19,7 +19,6 @@ class VersionSerializer(PassiveSerializer):
|
|||||||
|
|
||||||
version_current = SerializerMethodField()
|
version_current = SerializerMethodField()
|
||||||
version_latest = SerializerMethodField()
|
version_latest = SerializerMethodField()
|
||||||
version_latest_valid = SerializerMethodField()
|
|
||||||
build_hash = SerializerMethodField()
|
build_hash = SerializerMethodField()
|
||||||
outdated = SerializerMethodField()
|
outdated = SerializerMethodField()
|
||||||
|
|
||||||
@ -39,10 +38,6 @@ class VersionSerializer(PassiveSerializer):
|
|||||||
return __version__
|
return __version__
|
||||||
return version_in_cache
|
return version_in_cache
|
||||||
|
|
||||||
def get_version_latest_valid(self, _) -> bool:
|
|
||||||
"""Check if latest version is valid"""
|
|
||||||
return cache.get(VERSION_CACHE_KEY) != VERSION_NULL
|
|
||||||
|
|
||||||
def get_outdated(self, instance) -> bool:
|
def get_outdated(self, instance) -> bool:
|
||||||
"""Check if we're running the latest version"""
|
"""Check if we're running the latest version"""
|
||||||
return parse(self.get_version_current(instance)) < parse(self.get_version_latest(instance))
|
return parse(self.get_version_current(instance)) < parse(self.get_version_latest(instance))
|
||||||
|
|||||||
@ -18,7 +18,6 @@ from authentik.lib.utils.http import get_http_session
|
|||||||
from authentik.root.celery import CELERY_APP
|
from authentik.root.celery import CELERY_APP
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
VERSION_NULL = "0.0.0"
|
|
||||||
VERSION_CACHE_KEY = "authentik_latest_version"
|
VERSION_CACHE_KEY = "authentik_latest_version"
|
||||||
VERSION_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours
|
VERSION_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours
|
||||||
# Chop of the first ^ because we want to search the entire string
|
# Chop of the first ^ because we want to search the entire string
|
||||||
@ -56,7 +55,7 @@ def clear_update_notifications():
|
|||||||
def update_latest_version(self: SystemTask):
|
def update_latest_version(self: SystemTask):
|
||||||
"""Update latest version info"""
|
"""Update latest version info"""
|
||||||
if CONFIG.get_bool("disable_update_check"):
|
if CONFIG.get_bool("disable_update_check"):
|
||||||
cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
|
cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
|
||||||
self.set_status(TaskStatus.WARNING, "Version check disabled.")
|
self.set_status(TaskStatus.WARNING, "Version check disabled.")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
@ -83,7 +82,7 @@ def update_latest_version(self: SystemTask):
|
|||||||
event_dict["message"] = f"Changelog: {match.group()}"
|
event_dict["message"] = f"Changelog: {match.group()}"
|
||||||
Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
|
Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
|
||||||
except (RequestException, IndexError) as exc:
|
except (RequestException, IndexError) as exc:
|
||||||
cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
|
cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
|
||||||
self.set_error(exc)
|
self.set_error(exc)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,8 @@ from guardian.models import UserObjectPermission
|
|||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.serializers import BaseSerializer, Serializer
|
from rest_framework.serializers import BaseSerializer, Serializer
|
||||||
from structlog.stdlib import BoundLogger, get_logger
|
from structlog.stdlib import BoundLogger, get_logger
|
||||||
|
from structlog.testing import capture_logs
|
||||||
|
from structlog.types import EventDict
|
||||||
from yaml import load
|
from yaml import load
|
||||||
|
|
||||||
from authentik.blueprints.v1.common import (
|
from authentik.blueprints.v1.common import (
|
||||||
@ -40,7 +42,6 @@ from authentik.core.models import (
|
|||||||
from authentik.enterprise.license import LicenseKey
|
from authentik.enterprise.license import LicenseKey
|
||||||
from authentik.enterprise.models import LicenseUsage
|
from authentik.enterprise.models import LicenseUsage
|
||||||
from authentik.enterprise.providers.rac.models import ConnectionToken
|
from authentik.enterprise.providers.rac.models import ConnectionToken
|
||||||
from authentik.events.logs import LogEvent, capture_logs
|
|
||||||
from authentik.events.models import SystemTask
|
from authentik.events.models import SystemTask
|
||||||
from authentik.events.utils import cleanse_dict
|
from authentik.events.utils import cleanse_dict
|
||||||
from authentik.flows.models import FlowToken, Stage
|
from authentik.flows.models import FlowToken, Stage
|
||||||
@ -160,7 +161,7 @@ class Importer:
|
|||||||
|
|
||||||
def updater(value) -> Any:
|
def updater(value) -> Any:
|
||||||
if value in self.__pk_map:
|
if value in self.__pk_map:
|
||||||
self.logger.debug("Updating reference in entry", value=value)
|
self.logger.debug("updating reference in entry", value=value)
|
||||||
return self.__pk_map[value]
|
return self.__pk_map[value]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@ -249,7 +250,7 @@ class Importer:
|
|||||||
model_instance = existing_models.first()
|
model_instance = existing_models.first()
|
||||||
if not isinstance(model(), BaseMetaModel) and model_instance:
|
if not isinstance(model(), BaseMetaModel) and model_instance:
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
"Initialise serializer with instance",
|
"initialise serializer with instance",
|
||||||
model=model,
|
model=model,
|
||||||
instance=model_instance,
|
instance=model_instance,
|
||||||
pk=model_instance.pk,
|
pk=model_instance.pk,
|
||||||
@ -259,14 +260,14 @@ class Importer:
|
|||||||
elif model_instance and entry.state == BlueprintEntryDesiredState.MUST_CREATED:
|
elif model_instance and entry.state == BlueprintEntryDesiredState.MUST_CREATED:
|
||||||
raise EntryInvalidError.from_entry(
|
raise EntryInvalidError.from_entry(
|
||||||
(
|
(
|
||||||
f"State is set to {BlueprintEntryDesiredState.MUST_CREATED} "
|
f"state is set to {BlueprintEntryDesiredState.MUST_CREATED} "
|
||||||
"and object exists already",
|
"and object exists already",
|
||||||
),
|
),
|
||||||
entry,
|
entry,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
"Initialised new serializer instance",
|
"initialised new serializer instance",
|
||||||
model=model,
|
model=model,
|
||||||
**cleanse_dict(updated_identifiers),
|
**cleanse_dict(updated_identifiers),
|
||||||
)
|
)
|
||||||
@ -323,7 +324,7 @@ class Importer:
|
|||||||
model: type[SerializerModel] = registry.get_model(model_app_label, model_name)
|
model: type[SerializerModel] = registry.get_model(model_app_label, model_name)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
"App or Model does not exist", app=model_app_label, model=model_name
|
"app or model does not exist", app=model_app_label, model=model_name
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
# Validate each single entry
|
# Validate each single entry
|
||||||
@ -335,7 +336,7 @@ class Importer:
|
|||||||
if entry.get_state(self._import) == BlueprintEntryDesiredState.ABSENT:
|
if entry.get_state(self._import) == BlueprintEntryDesiredState.ABSENT:
|
||||||
serializer = exc.serializer
|
serializer = exc.serializer
|
||||||
else:
|
else:
|
||||||
self.logger.warning(f"Entry invalid: {exc}", entry=entry, error=exc)
|
self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc)
|
||||||
if raise_errors:
|
if raise_errors:
|
||||||
raise exc
|
raise exc
|
||||||
return False
|
return False
|
||||||
@ -355,14 +356,14 @@ class Importer:
|
|||||||
and state == BlueprintEntryDesiredState.CREATED
|
and state == BlueprintEntryDesiredState.CREATED
|
||||||
):
|
):
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
"Instance exists, skipping",
|
"instance exists, skipping",
|
||||||
model=model,
|
model=model,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
pk=instance.pk,
|
pk=instance.pk,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
instance = serializer.save()
|
instance = serializer.save()
|
||||||
self.logger.debug("Updated model", model=instance)
|
self.logger.debug("updated model", model=instance)
|
||||||
if "pk" in entry.identifiers:
|
if "pk" in entry.identifiers:
|
||||||
self.__pk_map[entry.identifiers["pk"]] = instance.pk
|
self.__pk_map[entry.identifiers["pk"]] = instance.pk
|
||||||
entry._state = BlueprintEntryState(instance)
|
entry._state = BlueprintEntryState(instance)
|
||||||
@ -370,12 +371,12 @@ class Importer:
|
|||||||
instance: Model | None = serializer.instance
|
instance: Model | None = serializer.instance
|
||||||
if instance.pk:
|
if instance.pk:
|
||||||
instance.delete()
|
instance.delete()
|
||||||
self.logger.debug("Deleted model", mode=instance)
|
self.logger.debug("deleted model", mode=instance)
|
||||||
continue
|
continue
|
||||||
self.logger.debug("Entry to delete with no instance, skipping")
|
self.logger.debug("entry to delete with no instance, skipping")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def validate(self, raise_validation_errors=False) -> tuple[bool, list[LogEvent]]:
|
def validate(self, raise_validation_errors=False) -> tuple[bool, list[EventDict]]:
|
||||||
"""Validate loaded blueprint export, ensure all models are allowed
|
"""Validate loaded blueprint export, ensure all models are allowed
|
||||||
and serializers have no errors"""
|
and serializers have no errors"""
|
||||||
self.logger.debug("Starting blueprint import validation")
|
self.logger.debug("Starting blueprint import validation")
|
||||||
@ -389,7 +390,9 @@ class Importer:
|
|||||||
):
|
):
|
||||||
successful = self._apply_models(raise_errors=raise_validation_errors)
|
successful = self._apply_models(raise_errors=raise_validation_errors)
|
||||||
if not successful:
|
if not successful:
|
||||||
self.logger.warning("Blueprint validation failed")
|
self.logger.debug("Blueprint validation failed")
|
||||||
|
for log in logs:
|
||||||
|
getattr(self.logger, log.get("log_level"))(**log)
|
||||||
self.logger.debug("Finished blueprint import validation")
|
self.logger.debug("Finished blueprint import validation")
|
||||||
self._import = orig_import
|
self._import = orig_import
|
||||||
return successful, logs
|
return successful, logs
|
||||||
|
|||||||
@ -30,7 +30,6 @@ from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, E
|
|||||||
from authentik.blueprints.v1.importer import Importer
|
from authentik.blueprints.v1.importer import Importer
|
||||||
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
|
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
|
||||||
from authentik.blueprints.v1.oci import OCI_PREFIX
|
from authentik.blueprints.v1.oci import OCI_PREFIX
|
||||||
from authentik.events.logs import capture_logs
|
|
||||||
from authentik.events.models import TaskStatus
|
from authentik.events.models import TaskStatus
|
||||||
from authentik.events.system_tasks import SystemTask, prefill_task
|
from authentik.events.system_tasks import SystemTask, prefill_task
|
||||||
from authentik.events.utils import sanitize_dict
|
from authentik.events.utils import sanitize_dict
|
||||||
@ -212,15 +211,14 @@ def apply_blueprint(self: SystemTask, instance_pk: str):
|
|||||||
if not valid:
|
if not valid:
|
||||||
instance.status = BlueprintInstanceStatus.ERROR
|
instance.status = BlueprintInstanceStatus.ERROR
|
||||||
instance.save()
|
instance.save()
|
||||||
self.set_status(TaskStatus.ERROR, *logs)
|
self.set_status(TaskStatus.ERROR, *[x["event"] for x in logs])
|
||||||
|
return
|
||||||
|
applied = importer.apply()
|
||||||
|
if not applied:
|
||||||
|
instance.status = BlueprintInstanceStatus.ERROR
|
||||||
|
instance.save()
|
||||||
|
self.set_status(TaskStatus.ERROR, "Failed to apply")
|
||||||
return
|
return
|
||||||
with capture_logs() as logs:
|
|
||||||
applied = importer.apply()
|
|
||||||
if not applied:
|
|
||||||
instance.status = BlueprintInstanceStatus.ERROR
|
|
||||||
instance.save()
|
|
||||||
self.set_status(TaskStatus.ERROR, *logs)
|
|
||||||
return
|
|
||||||
instance.status = BlueprintInstanceStatus.SUCCESSFUL
|
instance.status = BlueprintInstanceStatus.SUCCESSFUL
|
||||||
instance.last_applied_hash = file_hash
|
instance.last_applied_hash = file_hash
|
||||||
instance.last_applied = now()
|
instance.last_applied = now()
|
||||||
|
|||||||
@ -20,14 +20,15 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
from structlog.testing import capture_logs
|
||||||
|
|
||||||
from authentik.admin.api.metrics import CoordinateSerializer
|
from authentik.admin.api.metrics import CoordinateSerializer
|
||||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||||
from authentik.core.api.providers import ProviderSerializer
|
from authentik.core.api.providers import ProviderSerializer
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.models import Application, User
|
from authentik.core.models import Application, User
|
||||||
from authentik.events.logs import LogEventSerializer, capture_logs
|
|
||||||
from authentik.events.models import EventAction
|
from authentik.events.models import EventAction
|
||||||
|
from authentik.events.utils import sanitize_dict
|
||||||
from authentik.lib.utils.file import (
|
from authentik.lib.utils.file import (
|
||||||
FilePathSerializer,
|
FilePathSerializer,
|
||||||
FileUploadSerializer,
|
FileUploadSerializer,
|
||||||
@ -36,7 +37,7 @@ from authentik.lib.utils.file import (
|
|||||||
)
|
)
|
||||||
from authentik.policies.api.exec import PolicyTestResultSerializer
|
from authentik.policies.api.exec import PolicyTestResultSerializer
|
||||||
from authentik.policies.engine import PolicyEngine
|
from authentik.policies.engine import PolicyEngine
|
||||||
from authentik.policies.types import CACHE_PREFIX, PolicyResult
|
from authentik.policies.types import PolicyResult
|
||||||
from authentik.rbac.decorators import permission_required
|
from authentik.rbac.decorators import permission_required
|
||||||
from authentik.rbac.filters import ObjectFilter
|
from authentik.rbac.filters import ObjectFilter
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ LOGGER = get_logger()
|
|||||||
|
|
||||||
def user_app_cache_key(user_pk: str) -> str:
|
def user_app_cache_key(user_pk: str) -> str:
|
||||||
"""Cache key where application list for user is saved"""
|
"""Cache key where application list for user is saved"""
|
||||||
return f"{CACHE_PREFIX}/app_access/{user_pk}"
|
return f"goauthentik.io/core/app_access/{user_pk}"
|
||||||
|
|
||||||
|
|
||||||
class ApplicationSerializer(ModelSerializer):
|
class ApplicationSerializer(ModelSerializer):
|
||||||
@ -181,9 +182,9 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
|||||||
if request.user.is_superuser:
|
if request.user.is_superuser:
|
||||||
log_messages = []
|
log_messages = []
|
||||||
for log in logs:
|
for log in logs:
|
||||||
if log.attributes.get("process", "") == "PolicyProcess":
|
if log.get("process", "") == "PolicyProcess":
|
||||||
continue
|
continue
|
||||||
log_messages.append(LogEventSerializer(log).data)
|
log_messages.append(sanitize_dict(log))
|
||||||
result.log_messages = log_messages
|
result.log_messages = log_messages
|
||||||
response = PolicyTestResultSerializer(result)
|
response = PolicyTestResultSerializer(result)
|
||||||
return Response(response.data)
|
return Response(response.data)
|
||||||
@ -213,7 +214,7 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
|||||||
return super().list(request)
|
return super().list(request)
|
||||||
|
|
||||||
queryset = self._filter_queryset_for_list(self.get_queryset())
|
queryset = self._filter_queryset_for_list(self.get_queryset())
|
||||||
paginated_apps = self.paginate_queryset(queryset)
|
pagined_apps = self.paginate_queryset(queryset)
|
||||||
|
|
||||||
if "for_user" in request.query_params:
|
if "for_user" in request.query_params:
|
||||||
try:
|
try:
|
||||||
@ -227,18 +228,18 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
|||||||
raise ValidationError({"for_user": "User not found"})
|
raise ValidationError({"for_user": "User not found"})
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise ValidationError from exc
|
raise ValidationError from exc
|
||||||
allowed_applications = self._get_allowed_applications(paginated_apps, user=for_user)
|
allowed_applications = self._get_allowed_applications(pagined_apps, user=for_user)
|
||||||
serializer = self.get_serializer(allowed_applications, many=True)
|
serializer = self.get_serializer(allowed_applications, many=True)
|
||||||
return self.get_paginated_response(serializer.data)
|
return self.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
allowed_applications = []
|
allowed_applications = []
|
||||||
if not should_cache:
|
if not should_cache:
|
||||||
allowed_applications = self._get_allowed_applications(paginated_apps)
|
allowed_applications = self._get_allowed_applications(pagined_apps)
|
||||||
if should_cache:
|
if should_cache:
|
||||||
allowed_applications = cache.get(user_app_cache_key(self.request.user.pk))
|
allowed_applications = cache.get(user_app_cache_key(self.request.user.pk))
|
||||||
if not allowed_applications:
|
if not allowed_applications:
|
||||||
LOGGER.debug("Caching allowed application list")
|
LOGGER.debug("Caching allowed application list")
|
||||||
allowed_applications = self._get_allowed_applications(paginated_apps)
|
allowed_applications = self._get_allowed_applications(pagined_apps)
|
||||||
cache.set(
|
cache.set(
|
||||||
user_app_cache_key(self.request.user.pk),
|
user_app_cache_key(self.request.user.pk),
|
||||||
allowed_applications,
|
allowed_applications,
|
||||||
|
|||||||
@ -617,9 +617,6 @@ class UserSourceConnection(SerializerModel, CreatedUpdatedModel):
|
|||||||
"""Get serializer for this model"""
|
"""Get serializer for this model"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f"User-source connection (user={self.user.username}, source={self.source.slug})"
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (("user", "source"),)
|
unique_together = (("user", "source"),)
|
||||||
|
|
||||||
|
|||||||
@ -16,9 +16,8 @@ from authentik.core.models import Source, SourceUserMatchingModes, User, UserSou
|
|||||||
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION, PostUserEnrollmentStage
|
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION, PostUserEnrollmentStage
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.exceptions import FlowNonApplicableException
|
from authentik.flows.exceptions import FlowNonApplicableException
|
||||||
from authentik.flows.models import Flow, FlowToken, Stage, in_memory_stage
|
from authentik.flows.models import Flow, Stage, in_memory_stage
|
||||||
from authentik.flows.planner import (
|
from authentik.flows.planner import (
|
||||||
PLAN_CONTEXT_IS_RESTORED,
|
|
||||||
PLAN_CONTEXT_PENDING_USER,
|
PLAN_CONTEXT_PENDING_USER,
|
||||||
PLAN_CONTEXT_REDIRECT,
|
PLAN_CONTEXT_REDIRECT,
|
||||||
PLAN_CONTEXT_SOURCE,
|
PLAN_CONTEXT_SOURCE,
|
||||||
@ -36,8 +35,6 @@ from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
|||||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||||
from authentik.stages.user_write.stage import PLAN_CONTEXT_USER_PATH
|
from authentik.stages.user_write.stage import PLAN_CONTEXT_USER_PATH
|
||||||
|
|
||||||
SESSION_KEY_OVERRIDE_FLOW_TOKEN = "authentik/flows/source_override_flow_token" # nosec
|
|
||||||
|
|
||||||
|
|
||||||
class Action(Enum):
|
class Action(Enum):
|
||||||
"""Actions that can be decided based on the request
|
"""Actions that can be decided based on the request
|
||||||
@ -225,43 +222,22 @@ class SourceFlowManager:
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
"""Prepare Authentication Plan, redirect user FlowExecutor"""
|
"""Prepare Authentication Plan, redirect user FlowExecutor"""
|
||||||
|
# Ensure redirect is carried through when user was trying to
|
||||||
|
# authorize application
|
||||||
|
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
|
||||||
|
NEXT_ARG_NAME, "authentik_core:if-user"
|
||||||
|
)
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
{
|
{
|
||||||
# Since we authenticate the user by their token, they have no backend set
|
# Since we authenticate the user by their token, they have no backend set
|
||||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND: BACKEND_INBUILT,
|
PLAN_CONTEXT_AUTHENTICATION_BACKEND: BACKEND_INBUILT,
|
||||||
PLAN_CONTEXT_SSO: True,
|
PLAN_CONTEXT_SSO: True,
|
||||||
PLAN_CONTEXT_SOURCE: self.source,
|
PLAN_CONTEXT_SOURCE: self.source,
|
||||||
|
PLAN_CONTEXT_REDIRECT: final_redirect,
|
||||||
PLAN_CONTEXT_SOURCES_CONNECTION: connection,
|
PLAN_CONTEXT_SOURCES_CONNECTION: connection,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
kwargs.update(self.policy_context)
|
kwargs.update(self.policy_context)
|
||||||
if SESSION_KEY_OVERRIDE_FLOW_TOKEN in self.request.session:
|
|
||||||
token: FlowToken = self.request.session.get(SESSION_KEY_OVERRIDE_FLOW_TOKEN)
|
|
||||||
self._logger.info("Replacing source flow with overridden flow", flow=token.flow.slug)
|
|
||||||
plan = token.plan
|
|
||||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
|
|
||||||
plan.context.update(kwargs)
|
|
||||||
for stage in self.get_stages_to_append(flow):
|
|
||||||
plan.append_stage(stage)
|
|
||||||
if stages:
|
|
||||||
for stage in stages:
|
|
||||||
plan.append_stage(stage)
|
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
|
||||||
flow_slug = token.flow.slug
|
|
||||||
token.delete()
|
|
||||||
return redirect_with_qs(
|
|
||||||
"authentik_core:if-flow",
|
|
||||||
self.request.GET,
|
|
||||||
flow_slug=flow_slug,
|
|
||||||
)
|
|
||||||
# Ensure redirect is carried through when user was trying to
|
|
||||||
# authorize application
|
|
||||||
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
|
|
||||||
NEXT_ARG_NAME, "authentik_core:if-user"
|
|
||||||
)
|
|
||||||
if PLAN_CONTEXT_REDIRECT not in kwargs:
|
|
||||||
kwargs[PLAN_CONTEXT_REDIRECT] = final_redirect
|
|
||||||
|
|
||||||
if not flow:
|
if not flow:
|
||||||
return bad_request_message(
|
return bad_request_message(
|
||||||
self.request,
|
self.request,
|
||||||
|
|||||||
@ -6,13 +6,13 @@ from rest_framework.filters import OrderingFilter, SearchFilter
|
|||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
|
||||||
from authentik.api.authorization import OwnerFilter, OwnerSuperuserPermissions
|
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
||||||
from authentik.core.api.groups import GroupMemberSerializer
|
from authentik.core.api.groups import GroupMemberSerializer
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||||
from authentik.enterprise.providers.rac.api.endpoints import EndpointSerializer
|
from authentik.enterprise.providers.rac.api.endpoints import EndpointSerializer
|
||||||
from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer
|
from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer
|
||||||
from authentik.enterprise.providers.rac.models import ConnectionToken
|
from authentik.enterprise.providers.rac.models import ConnectionToken, Endpoint
|
||||||
|
|
||||||
|
|
||||||
class ConnectionTokenSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
class ConnectionTokenSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
||||||
@ -23,7 +23,7 @@ class ConnectionTokenSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
|||||||
user = GroupMemberSerializer(source="session.user", read_only=True)
|
user = GroupMemberSerializer(source="session.user", read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ConnectionToken
|
model = Endpoint
|
||||||
fields = [
|
fields = [
|
||||||
"pk",
|
"pk",
|
||||||
"provider",
|
"provider",
|
||||||
@ -49,5 +49,5 @@ class ConnectionTokenViewSet(
|
|||||||
filterset_fields = ["endpoint", "session__user", "provider"]
|
filterset_fields = ["endpoint", "session__user", "provider"]
|
||||||
search_fields = ["endpoint__name", "provider__name"]
|
search_fields = ["endpoint__name", "provider__name"]
|
||||||
ordering = ["endpoint__name", "provider__name"]
|
ordering = ["endpoint__name", "provider__name"]
|
||||||
permission_classes = [OwnerSuperuserPermissions]
|
permission_classes = [OwnerPermissions]
|
||||||
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||||
|
|||||||
@ -15,7 +15,6 @@ CELERY_BEAT_SCHEDULE = {
|
|||||||
TENANT_APPS = [
|
TENANT_APPS = [
|
||||||
"authentik.enterprise.audit",
|
"authentik.enterprise.audit",
|
||||||
"authentik.enterprise.providers.rac",
|
"authentik.enterprise.providers.rac",
|
||||||
"authentik.enterprise.stages.source",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = ["authentik.enterprise.middleware.EnterpriseMiddleware"]
|
MIDDLEWARE = ["authentik.enterprise.middleware.EnterpriseMiddleware"]
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
"""Source Stage API Views"""
|
|
||||||
|
|
||||||
from rest_framework.exceptions import ValidationError
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
|
||||||
|
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
|
||||||
from authentik.core.models import Source
|
|
||||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
|
||||||
from authentik.enterprise.stages.source.models import SourceStage
|
|
||||||
from authentik.flows.api.stages import StageSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class SourceStageSerializer(EnterpriseRequiredMixin, StageSerializer):
|
|
||||||
"""SourceStage Serializer"""
|
|
||||||
|
|
||||||
def validate_source(self, _source: Source) -> Source:
|
|
||||||
"""Ensure configured source supports web-based login"""
|
|
||||||
source = Source.objects.filter(pk=_source.pk).select_subclasses().first()
|
|
||||||
if not source:
|
|
||||||
raise ValidationError("Invalid source")
|
|
||||||
login_button = source.ui_login_button(self.context["request"])
|
|
||||||
if not login_button:
|
|
||||||
raise ValidationError("Invalid source selected, only web-based sources are supported.")
|
|
||||||
return source
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = SourceStage
|
|
||||||
fields = StageSerializer.Meta.fields + ["source", "resume_timeout"]
|
|
||||||
|
|
||||||
|
|
||||||
class SourceStageViewSet(UsedByMixin, ModelViewSet):
|
|
||||||
"""SourceStage Viewset"""
|
|
||||||
|
|
||||||
queryset = SourceStage.objects.all()
|
|
||||||
serializer_class = SourceStageSerializer
|
|
||||||
filterset_fields = "__all__"
|
|
||||||
ordering = ["name"]
|
|
||||||
search_fields = ["name"]
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
"""authentik stage app config"""
|
|
||||||
|
|
||||||
from authentik.enterprise.apps import EnterpriseConfig
|
|
||||||
|
|
||||||
|
|
||||||
class AuthentikEnterpriseStageSourceConfig(EnterpriseConfig):
|
|
||||||
"""authentik source stage config"""
|
|
||||||
|
|
||||||
name = "authentik.enterprise.stages.source"
|
|
||||||
label = "authentik_stages_source"
|
|
||||||
verbose_name = "authentik Enterprise.Stages.Source"
|
|
||||||
default = True
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
# Generated by Django 5.0.2 on 2024-02-25 20:44
|
|
||||||
|
|
||||||
import authentik.lib.utils.time
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_core", "0033_alter_user_options"),
|
|
||||||
("authentik_flows", "0027_auto_20231028_1424"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="SourceStage",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"stage_ptr",
|
|
||||||
models.OneToOneField(
|
|
||||||
auto_created=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
parent_link=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
to="authentik_flows.stage",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"resume_timeout",
|
|
||||||
models.TextField(
|
|
||||||
default="minutes=10",
|
|
||||||
help_text="Amount of time a user can take to return from the source to continue the flow (Format: hours=-1;minutes=-2;seconds=-3)",
|
|
||||||
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"source",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE, to="authentik_core.source"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"verbose_name": "Source Stage",
|
|
||||||
"verbose_name_plural": "Source Stages",
|
|
||||||
},
|
|
||||||
bases=("authentik_flows.stage",),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
"""Source stage models"""
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.views import View
|
|
||||||
from rest_framework.serializers import BaseSerializer
|
|
||||||
|
|
||||||
from authentik.flows.models import Stage
|
|
||||||
from authentik.lib.utils.time import timedelta_string_validator
|
|
||||||
|
|
||||||
|
|
||||||
class SourceStage(Stage):
|
|
||||||
"""Suspend the current flow execution and send the user to a source,
|
|
||||||
after which this flow execution is resumed."""
|
|
||||||
|
|
||||||
source = models.ForeignKey("authentik_core.Source", on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
resume_timeout = models.TextField(
|
|
||||||
default="minutes=10",
|
|
||||||
validators=[timedelta_string_validator],
|
|
||||||
help_text=_(
|
|
||||||
"Amount of time a user can take to return from the source to continue the flow "
|
|
||||||
"(Format: hours=-1;minutes=-2;seconds=-3)"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def serializer(self) -> type[BaseSerializer]:
|
|
||||||
from authentik.enterprise.stages.source.api import SourceStageSerializer
|
|
||||||
|
|
||||||
return SourceStageSerializer
|
|
||||||
|
|
||||||
@property
|
|
||||||
def view(self) -> type[View]:
|
|
||||||
from authentik.enterprise.stages.source.stage import SourceStageView
|
|
||||||
|
|
||||||
return SourceStageView
|
|
||||||
|
|
||||||
@property
|
|
||||||
def component(self) -> str:
|
|
||||||
return "ak-stage-source-form"
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("Source Stage")
|
|
||||||
verbose_name_plural = _("Source Stages")
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
"""Source stage logic"""
|
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
|
||||||
from django.utils.text import slugify
|
|
||||||
from django.utils.timezone import now
|
|
||||||
from guardian.shortcuts import get_anonymous_user
|
|
||||||
|
|
||||||
from authentik.core.models import Source, User
|
|
||||||
from authentik.core.sources.flow_manager import SESSION_KEY_OVERRIDE_FLOW_TOKEN
|
|
||||||
from authentik.core.types import UILoginButton
|
|
||||||
from authentik.enterprise.stages.source.models import SourceStage
|
|
||||||
from authentik.flows.challenge import Challenge, ChallengeResponse
|
|
||||||
from authentik.flows.models import FlowToken
|
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED
|
|
||||||
from authentik.flows.stage import ChallengeStageView
|
|
||||||
from authentik.lib.utils.time import timedelta_from_string
|
|
||||||
|
|
||||||
PLAN_CONTEXT_RESUME_TOKEN = "resume_token" # nosec
|
|
||||||
|
|
||||||
|
|
||||||
class SourceStageView(ChallengeStageView):
|
|
||||||
"""Suspend the current flow execution and send the user to a source,
|
|
||||||
after which this flow execution is resumed."""
|
|
||||||
|
|
||||||
login_button: UILoginButton
|
|
||||||
|
|
||||||
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
|
||||||
current_stage: SourceStage = self.executor.current_stage
|
|
||||||
source: Source = (
|
|
||||||
Source.objects.filter(pk=current_stage.source_id).select_subclasses().first()
|
|
||||||
)
|
|
||||||
if not source:
|
|
||||||
self.logger.warning("Source does not exist")
|
|
||||||
return self.executor.stage_invalid("Source does not exist")
|
|
||||||
self.login_button = source.ui_login_button(self.request)
|
|
||||||
if not self.login_button:
|
|
||||||
self.logger.warning("Source does not have a UI login button")
|
|
||||||
return self.executor.stage_invalid("Invalid source")
|
|
||||||
restore_token = self.executor.plan.context.get(PLAN_CONTEXT_IS_RESTORED)
|
|
||||||
override_token = self.request.session.get(SESSION_KEY_OVERRIDE_FLOW_TOKEN)
|
|
||||||
if restore_token and override_token and restore_token.pk == override_token.pk:
|
|
||||||
del self.request.session[SESSION_KEY_OVERRIDE_FLOW_TOKEN]
|
|
||||||
return self.executor.stage_ok()
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get_challenge(self, *args, **kwargs) -> Challenge:
|
|
||||||
resume_token = self.create_flow_token()
|
|
||||||
self.request.session[SESSION_KEY_OVERRIDE_FLOW_TOKEN] = resume_token
|
|
||||||
return self.login_button.challenge
|
|
||||||
|
|
||||||
def create_flow_token(self) -> FlowToken:
|
|
||||||
"""Save the current flow state in a token that can be used to resume this flow"""
|
|
||||||
pending_user: User = self.get_pending_user()
|
|
||||||
if pending_user.is_anonymous:
|
|
||||||
pending_user = get_anonymous_user()
|
|
||||||
current_stage: SourceStage = self.executor.current_stage
|
|
||||||
identifier = slugify(f"ak-source-stage-{current_stage.name}-{str(uuid4())}")
|
|
||||||
# Don't check for validity here, we only care if the token exists
|
|
||||||
tokens = FlowToken.objects.filter(identifier=identifier)
|
|
||||||
valid_delta = timedelta_from_string(current_stage.resume_timeout)
|
|
||||||
if not tokens.exists():
|
|
||||||
return FlowToken.objects.create(
|
|
||||||
expires=now() + valid_delta,
|
|
||||||
user=pending_user,
|
|
||||||
identifier=identifier,
|
|
||||||
flow=self.executor.flow,
|
|
||||||
_plan=FlowToken.pickle(self.executor.plan),
|
|
||||||
)
|
|
||||||
token = tokens.first()
|
|
||||||
# Check if token is expired and rotate key if so
|
|
||||||
if token.is_expired:
|
|
||||||
token.expire_action()
|
|
||||||
return token
|
|
||||||
|
|
||||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
|
||||||
return self.executor.stage_ok()
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
"""Source stage tests"""
|
|
||||||
|
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
from authentik.core.tests.utils import create_test_flow, create_test_user
|
|
||||||
from authentik.enterprise.stages.source.models import SourceStage
|
|
||||||
from authentik.flows.models import FlowDesignation, FlowStageBinding, FlowToken
|
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, FlowPlan
|
|
||||||
from authentik.flows.tests import FlowTestCase
|
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
|
||||||
from authentik.lib.generators import generate_id
|
|
||||||
from authentik.sources.saml.models import SAMLSource
|
|
||||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
|
||||||
from authentik.stages.password import BACKEND_INBUILT
|
|
||||||
from authentik.stages.password.models import PasswordStage
|
|
||||||
from authentik.stages.user_login.models import UserLoginStage
|
|
||||||
|
|
||||||
|
|
||||||
class TestSourceStage(FlowTestCase):
|
|
||||||
"""Source stage tests"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.source = SAMLSource.objects.create(
|
|
||||||
slug=generate_id(),
|
|
||||||
issuer="authentik",
|
|
||||||
allow_idp_initiated=True,
|
|
||||||
pre_authentication_flow=create_test_flow(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_source_success(self):
|
|
||||||
"""Test"""
|
|
||||||
user = create_test_user()
|
|
||||||
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
|
||||||
stage = SourceStage.objects.create(name=generate_id(), source=self.source)
|
|
||||||
FlowStageBinding.objects.create(
|
|
||||||
target=flow,
|
|
||||||
stage=IdentificationStage.objects.create(
|
|
||||||
name=generate_id(),
|
|
||||||
user_fields=[UserFields.USERNAME],
|
|
||||||
),
|
|
||||||
order=0,
|
|
||||||
)
|
|
||||||
FlowStageBinding.objects.create(
|
|
||||||
target=flow,
|
|
||||||
stage=PasswordStage.objects.create(name=generate_id(), backends=[BACKEND_INBUILT]),
|
|
||||||
order=5,
|
|
||||||
)
|
|
||||||
FlowStageBinding.objects.create(target=flow, stage=stage, order=10)
|
|
||||||
FlowStageBinding.objects.create(
|
|
||||||
target=flow,
|
|
||||||
stage=UserLoginStage.objects.create(
|
|
||||||
name=generate_id(),
|
|
||||||
),
|
|
||||||
order=15,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get user identification stage
|
|
||||||
response = self.client.get(
|
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertStageResponse(response, flow, component="ak-stage-identification")
|
|
||||||
# Send username
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
|
||||||
data={"uid_field": user.username},
|
|
||||||
follow=True,
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertStageResponse(response, flow, component="ak-stage-password")
|
|
||||||
# Send password
|
|
||||||
response = self.client.post(
|
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
|
||||||
data={"password": user.username},
|
|
||||||
follow=True,
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertStageRedirects(
|
|
||||||
response,
|
|
||||||
reverse("authentik_sources_saml:login", kwargs={"source_slug": self.source.slug}),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Hijack flow plan so we don't have to emulate the source
|
|
||||||
flow_token = FlowToken.objects.filter(
|
|
||||||
identifier__startswith=f"ak-source-stage-{stage.name.lower()}"
|
|
||||||
).first()
|
|
||||||
self.assertIsNotNone(flow_token)
|
|
||||||
session = self.client.session
|
|
||||||
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
|
||||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = flow_token
|
|
||||||
session[SESSION_KEY_PLAN] = plan
|
|
||||||
session.save()
|
|
||||||
|
|
||||||
# Pretend we've just returned from the source
|
|
||||||
response = self.client.get(
|
|
||||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), follow=True
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
"""API URLs"""
|
|
||||||
|
|
||||||
from authentik.enterprise.stages.source.api import SourceStageViewSet
|
|
||||||
|
|
||||||
api_urlpatterns = [("stages/source", SourceStageViewSet)]
|
|
||||||
@ -12,6 +12,7 @@ from rest_framework.fields import (
|
|||||||
ChoiceField,
|
ChoiceField,
|
||||||
DateTimeField,
|
DateTimeField,
|
||||||
FloatField,
|
FloatField,
|
||||||
|
ListField,
|
||||||
SerializerMethodField,
|
SerializerMethodField,
|
||||||
)
|
)
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
@ -20,7 +21,6 @@ from rest_framework.serializers import ModelSerializer
|
|||||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.events.logs import LogEventSerializer
|
|
||||||
from authentik.events.models import SystemTask, TaskStatus
|
from authentik.events.models import SystemTask, TaskStatus
|
||||||
from authentik.rbac.decorators import permission_required
|
from authentik.rbac.decorators import permission_required
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ class SystemTaskSerializer(ModelSerializer):
|
|||||||
duration = FloatField(read_only=True)
|
duration = FloatField(read_only=True)
|
||||||
|
|
||||||
status = ChoiceField(choices=[(x.value, x.name) for x in TaskStatus])
|
status = ChoiceField(choices=[(x.value, x.name) for x in TaskStatus])
|
||||||
messages = LogEventSerializer(many=True)
|
messages = ListField(child=CharField())
|
||||||
|
|
||||||
def get_full_name(self, instance: SystemTask) -> str:
|
def get_full_name(self, instance: SystemTask) -> str:
|
||||||
"""Get full name with UID"""
|
"""Get full name with UID"""
|
||||||
|
|||||||
@ -1,82 +0,0 @@
|
|||||||
from collections.abc import Generator
|
|
||||||
from contextlib import contextmanager
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from django.utils.timezone import now
|
|
||||||
from rest_framework.fields import CharField, ChoiceField, DateTimeField, DictField
|
|
||||||
from structlog import configure, get_config
|
|
||||||
from structlog.stdlib import NAME_TO_LEVEL, ProcessorFormatter
|
|
||||||
from structlog.testing import LogCapture
|
|
||||||
from structlog.types import EventDict
|
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
|
||||||
from authentik.events.utils import sanitize_dict
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
|
||||||
class LogEvent:
|
|
||||||
|
|
||||||
event: str
|
|
||||||
log_level: str
|
|
||||||
logger: str
|
|
||||||
timestamp: datetime = field(default_factory=now)
|
|
||||||
attributes: dict[str, Any] = field(default_factory=dict)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_event_dict(item: EventDict) -> "LogEvent":
|
|
||||||
event = item.pop("event")
|
|
||||||
log_level = item.pop("level").lower()
|
|
||||||
timestamp = datetime.fromisoformat(item.pop("timestamp"))
|
|
||||||
item.pop("pid", None)
|
|
||||||
# Sometimes log entries have both `level` and `log_level` set, but `level` is always set
|
|
||||||
item.pop("log_level", None)
|
|
||||||
return LogEvent(
|
|
||||||
event, log_level, item.pop("logger"), timestamp, attributes=sanitize_dict(item)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LogEventSerializer(PassiveSerializer):
|
|
||||||
"""Single log message with all context logged."""
|
|
||||||
|
|
||||||
timestamp = DateTimeField()
|
|
||||||
log_level = ChoiceField(choices=tuple((x, x) for x in NAME_TO_LEVEL.keys()))
|
|
||||||
logger = CharField()
|
|
||||||
event = CharField()
|
|
||||||
attributes = DictField()
|
|
||||||
|
|
||||||
# TODO(2024.6?): This is a migration helper to return a correct API response for logs that
|
|
||||||
# have been saved in an older format (mostly just list[str] with just the messages)
|
|
||||||
def to_representation(self, instance):
|
|
||||||
if isinstance(instance, str):
|
|
||||||
instance = LogEvent(instance, "", "")
|
|
||||||
elif isinstance(instance, list):
|
|
||||||
instance = [LogEvent(x, "", "") for x in instance]
|
|
||||||
return super().to_representation(instance)
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def capture_logs(log_default_output=True) -> Generator[list[LogEvent], None, None]:
|
|
||||||
"""Capture log entries created"""
|
|
||||||
logs = []
|
|
||||||
cap = LogCapture()
|
|
||||||
# Modify `_Configuration.default_processors` set via `configure` but always
|
|
||||||
# keep the list instance intact to not break references held by bound
|
|
||||||
# loggers.
|
|
||||||
processors: list = get_config()["processors"]
|
|
||||||
old_processors = processors.copy()
|
|
||||||
try:
|
|
||||||
# clear processors list and use LogCapture for testing
|
|
||||||
if ProcessorFormatter.wrap_for_formatter in processors:
|
|
||||||
processors.remove(ProcessorFormatter.wrap_for_formatter)
|
|
||||||
processors.append(cap)
|
|
||||||
configure(processors=processors)
|
|
||||||
yield logs
|
|
||||||
for raw_log in cap.entries:
|
|
||||||
logs.append(LogEvent.from_event_dict(raw_log))
|
|
||||||
finally:
|
|
||||||
# remove LogCapture and restore original processors
|
|
||||||
processors.clear()
|
|
||||||
processors.extend(old_processors)
|
|
||||||
configure(processors=processors)
|
|
||||||
@ -452,13 +452,6 @@ class NotificationTransport(SerializerModel):
|
|||||||
|
|
||||||
def send_email(self, notification: "Notification") -> list[str]:
|
def send_email(self, notification: "Notification") -> list[str]:
|
||||||
"""Send notification via global email configuration"""
|
"""Send notification via global email configuration"""
|
||||||
if notification.user.email.strip() == "":
|
|
||||||
LOGGER.info(
|
|
||||||
"Discarding notification as user has no email address",
|
|
||||||
user=notification.user,
|
|
||||||
notification=notification,
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
subject_prefix = "authentik Notification: "
|
subject_prefix = "authentik Notification: "
|
||||||
context = {
|
context = {
|
||||||
"key_value": {
|
"key_value": {
|
||||||
|
|||||||
@ -9,7 +9,6 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
from tenant_schemas_celery.task import TenantTask
|
from tenant_schemas_celery.task import TenantTask
|
||||||
|
|
||||||
from authentik.events.logs import LogEvent
|
|
||||||
from authentik.events.models import Event, EventAction, TaskStatus
|
from authentik.events.models import Event, EventAction, TaskStatus
|
||||||
from authentik.events.models import SystemTask as DBSystemTask
|
from authentik.events.models import SystemTask as DBSystemTask
|
||||||
from authentik.events.utils import sanitize_item
|
from authentik.events.utils import sanitize_item
|
||||||
@ -25,7 +24,7 @@ class SystemTask(TenantTask):
|
|||||||
save_on_success: bool
|
save_on_success: bool
|
||||||
|
|
||||||
_status: TaskStatus
|
_status: TaskStatus
|
||||||
_messages: list[LogEvent]
|
_messages: list[str]
|
||||||
|
|
||||||
_uid: str | None
|
_uid: str | None
|
||||||
# Precise start time from perf_counter
|
# Precise start time from perf_counter
|
||||||
@ -45,20 +44,15 @@ class SystemTask(TenantTask):
|
|||||||
"""Set UID, so in the case of an unexpected error its saved correctly"""
|
"""Set UID, so in the case of an unexpected error its saved correctly"""
|
||||||
self._uid = uid
|
self._uid = uid
|
||||||
|
|
||||||
def set_status(self, status: TaskStatus, *messages: LogEvent):
|
def set_status(self, status: TaskStatus, *messages: str):
|
||||||
"""Set result for current run, will overwrite previous result."""
|
"""Set result for current run, will overwrite previous result."""
|
||||||
self._status = status
|
self._status = status
|
||||||
self._messages = list(messages)
|
self._messages = messages
|
||||||
for idx, msg in enumerate(self._messages):
|
|
||||||
if not isinstance(msg, LogEvent):
|
|
||||||
self._messages[idx] = LogEvent(msg, logger=self.__name__, log_level="info")
|
|
||||||
|
|
||||||
def set_error(self, exception: Exception):
|
def set_error(self, exception: Exception):
|
||||||
"""Set result to error and save exception"""
|
"""Set result to error and save exception"""
|
||||||
self._status = TaskStatus.ERROR
|
self._status = TaskStatus.ERROR
|
||||||
self._messages = [
|
self._messages = [exception_to_string(exception)]
|
||||||
LogEvent(exception_to_string(exception), logger=self.__name__, log_level="error")
|
|
||||||
]
|
|
||||||
|
|
||||||
def before_start(self, task_id, args, kwargs):
|
def before_start(self, task_id, args, kwargs):
|
||||||
self._start_precise = perf_counter()
|
self._start_precise = perf_counter()
|
||||||
@ -104,7 +98,8 @@ class SystemTask(TenantTask):
|
|||||||
def on_failure(self, exc, task_id, args, kwargs, einfo):
|
def on_failure(self, exc, task_id, args, kwargs, einfo):
|
||||||
super().on_failure(exc, task_id, args, kwargs, einfo=einfo)
|
super().on_failure(exc, task_id, args, kwargs, einfo=einfo)
|
||||||
if not self._status:
|
if not self._status:
|
||||||
self.set_error(exc)
|
self._status = TaskStatus.ERROR
|
||||||
|
self._messages = exception_to_string(exc)
|
||||||
DBSystemTask.objects.update_or_create(
|
DBSystemTask.objects.update_or_create(
|
||||||
name=self.__name__,
|
name=self.__name__,
|
||||||
uid=self._uid,
|
uid=self._uid,
|
||||||
|
|||||||
@ -47,4 +47,3 @@ class FlowStageBindingViewSet(UsedByMixin, ModelViewSet):
|
|||||||
filterset_fields = "__all__"
|
filterset_fields = "__all__"
|
||||||
search_fields = ["stage__name"]
|
search_fields = ["stage__name"]
|
||||||
ordering = ["order"]
|
ordering = ["order"]
|
||||||
ordering_fields = ["order", "stage__name"]
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from django.utils.translation import gettext as _
|
|||||||
from drf_spectacular.types import OpenApiTypes
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.fields import BooleanField, CharField, ReadOnlyField
|
from rest_framework.fields import BooleanField, CharField, DictField, ListField, ReadOnlyField
|
||||||
from rest_framework.parsers import MultiPartParser
|
from rest_framework.parsers import MultiPartParser
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@ -19,7 +19,7 @@ from authentik.blueprints.v1.exporter import FlowExporter
|
|||||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, Importer
|
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, Importer
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import CacheSerializer, LinkSerializer, PassiveSerializer
|
from authentik.core.api.utils import CacheSerializer, LinkSerializer, PassiveSerializer
|
||||||
from authentik.events.logs import LogEventSerializer
|
from authentik.events.utils import sanitize_dict
|
||||||
from authentik.flows.api.flows_diagram import FlowDiagram, FlowDiagramSerializer
|
from authentik.flows.api.flows_diagram import FlowDiagram, FlowDiagramSerializer
|
||||||
from authentik.flows.exceptions import FlowNonApplicableException
|
from authentik.flows.exceptions import FlowNonApplicableException
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
@ -107,7 +107,7 @@ class FlowSetSerializer(FlowSerializer):
|
|||||||
class FlowImportResultSerializer(PassiveSerializer):
|
class FlowImportResultSerializer(PassiveSerializer):
|
||||||
"""Logs of an attempted flow import"""
|
"""Logs of an attempted flow import"""
|
||||||
|
|
||||||
logs = LogEventSerializer(many=True, read_only=True)
|
logs = ListField(child=DictField(), read_only=True)
|
||||||
success = BooleanField(read_only=True)
|
success = BooleanField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
|
|||||||
|
|
||||||
importer = Importer.from_string(file.read().decode())
|
importer = Importer.from_string(file.read().decode())
|
||||||
valid, logs = importer.validate()
|
valid, logs = importer.validate()
|
||||||
import_response.initial_data["logs"] = [LogEventSerializer(log).data for log in logs]
|
import_response.initial_data["logs"] = [sanitize_dict(log) for log in logs]
|
||||||
import_response.initial_data["success"] = valid
|
import_response.initial_data["success"] = valid
|
||||||
import_response.is_valid()
|
import_response.is_valid()
|
||||||
if not valid:
|
if not valid:
|
||||||
|
|||||||
@ -13,7 +13,6 @@ from structlog.stdlib import get_logger
|
|||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.enterprise.apps import EnterpriseConfig
|
|
||||||
from authentik.flows.api.flows import FlowSetSerializer
|
from authentik.flows.api.flows import FlowSetSerializer
|
||||||
from authentik.flows.models import ConfigurableStage, Stage
|
from authentik.flows.models import ConfigurableStage, Stage
|
||||||
from authentik.lib.utils.reflection import all_subclasses
|
from authentik.lib.utils.reflection import all_subclasses
|
||||||
@ -76,7 +75,6 @@ class StageViewSet(
|
|||||||
"description": subclass.__doc__,
|
"description": subclass.__doc__,
|
||||||
"component": subclass().component,
|
"component": subclass().component,
|
||||||
"model_name": subclass._meta.model_name,
|
"model_name": subclass._meta.model_name,
|
||||||
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
data = sorted(data, key=lambda x: x["name"])
|
data = sorted(data, key=lambda x: x["name"])
|
||||||
|
|||||||
@ -59,11 +59,11 @@ class FlowPlan:
|
|||||||
markers: list[StageMarker] = field(default_factory=list)
|
markers: list[StageMarker] = field(default_factory=list)
|
||||||
|
|
||||||
def append_stage(self, stage: Stage, marker: StageMarker | None = None):
|
def append_stage(self, stage: Stage, marker: StageMarker | None = None):
|
||||||
"""Append `stage` to the end of the plan, optionally with stage marker"""
|
"""Append `stage` to all stages, optionally with stage marker"""
|
||||||
return self.append(FlowStageBinding(stage=stage), marker)
|
return self.append(FlowStageBinding(stage=stage), marker)
|
||||||
|
|
||||||
def append(self, binding: FlowStageBinding, marker: StageMarker | None = None):
|
def append(self, binding: FlowStageBinding, marker: StageMarker | None = None):
|
||||||
"""Append `stage` to the end of the plan, optionally with stage marker"""
|
"""Append `stage` to all stages, optionally with stage marker"""
|
||||||
self.bindings.append(binding)
|
self.bindings.append(binding)
|
||||||
self.markers.append(marker or StageMarker())
|
self.markers.append(marker or StageMarker())
|
||||||
|
|
||||||
|
|||||||
@ -53,7 +53,6 @@ class TestFlowInspector(APITestCase):
|
|||||||
"title": flow.title,
|
"title": flow.title,
|
||||||
"layout": "stacked",
|
"layout": "stacked",
|
||||||
},
|
},
|
||||||
"flow_designation": "authentication",
|
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
"password_fields": False,
|
"password_fields": False,
|
||||||
"primary_action": "Log in",
|
"primary_action": "Log in",
|
||||||
|
|||||||
@ -450,7 +450,7 @@ class FlowExecutorView(APIView):
|
|||||||
return to_stage_response(self.request, challenge_view.get(self.request))
|
return to_stage_response(self.request, challenge_view.get(self.request))
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
"""Cancel current flow execution"""
|
"""Cancel current execution and return a redirect"""
|
||||||
keys_to_delete = [
|
keys_to_delete = [
|
||||||
SESSION_KEY_APPLICATION_PRE,
|
SESSION_KEY_APPLICATION_PRE,
|
||||||
SESSION_KEY_PLAN,
|
SESSION_KEY_PLAN,
|
||||||
@ -469,7 +469,7 @@ class FlowExecutorView(APIView):
|
|||||||
|
|
||||||
|
|
||||||
class CancelView(View):
|
class CancelView(View):
|
||||||
"""View which cancels the currently active plan"""
|
"""View which canels the currently active plan"""
|
||||||
|
|
||||||
def get(self, request: HttpRequest) -> HttpResponse:
|
def get(self, request: HttpRequest) -> HttpResponse:
|
||||||
"""View which canels the currently active plan"""
|
"""View which canels the currently active plan"""
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
from structlog.testing import capture_logs
|
||||||
|
|
||||||
from authentik import __version__, get_build_hash
|
from authentik import __version__, get_build_hash
|
||||||
from authentik.events.logs import LogEvent, capture_logs
|
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.sentry import SentryIgnoredException
|
from authentik.lib.sentry import SentryIgnoredException
|
||||||
from authentik.outposts.models import (
|
from authentik.outposts.models import (
|
||||||
@ -63,21 +63,21 @@ class BaseController:
|
|||||||
"""Called by scheduled task to reconcile deployment/service/etc"""
|
"""Called by scheduled task to reconcile deployment/service/etc"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def up_with_logs(self) -> list[LogEvent]:
|
def up_with_logs(self) -> list[str]:
|
||||||
"""Call .up() but capture all log output and return it."""
|
"""Call .up() but capture all log output and return it."""
|
||||||
with capture_logs() as logs:
|
with capture_logs() as logs:
|
||||||
self.up()
|
self.up()
|
||||||
return logs
|
return [x["event"] for x in logs]
|
||||||
|
|
||||||
def down(self):
|
def down(self):
|
||||||
"""Handler to delete everything we've created"""
|
"""Handler to delete everything we've created"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def down_with_logs(self) -> list[LogEvent]:
|
def down_with_logs(self) -> list[str]:
|
||||||
"""Call .down() but capture all log output and return it."""
|
"""Call .down() but capture all log output and return it."""
|
||||||
with capture_logs() as logs:
|
with capture_logs() as logs:
|
||||||
self.down()
|
self.down()
|
||||||
return logs
|
return [x["event"] for x in logs]
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|||||||
@ -33,8 +33,6 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
|
|||||||
# priority than being updated.
|
# priority than being updated.
|
||||||
if current.spec.selector != reference.spec.selector:
|
if current.spec.selector != reference.spec.selector:
|
||||||
raise NeedsUpdate()
|
raise NeedsUpdate()
|
||||||
if current.spec.type != reference.spec.type:
|
|
||||||
raise NeedsUpdate()
|
|
||||||
super().reconcile(current, reference)
|
super().reconcile(current, reference)
|
||||||
|
|
||||||
def get_reference_object(self) -> V1Service:
|
def get_reference_object(self) -> V1Service:
|
||||||
|
|||||||
@ -9,10 +9,10 @@ from kubernetes.client.exceptions import OpenApiException
|
|||||||
from kubernetes.config.config_exception import ConfigException
|
from kubernetes.config.config_exception import ConfigException
|
||||||
from kubernetes.config.incluster_config import load_incluster_config
|
from kubernetes.config.incluster_config import load_incluster_config
|
||||||
from kubernetes.config.kube_config import load_kube_config_from_dict
|
from kubernetes.config.kube_config import load_kube_config_from_dict
|
||||||
|
from structlog.testing import capture_logs
|
||||||
from urllib3.exceptions import HTTPError
|
from urllib3.exceptions import HTTPError
|
||||||
from yaml import dump_all
|
from yaml import dump_all
|
||||||
|
|
||||||
from authentik.events.logs import LogEvent, capture_logs
|
|
||||||
from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException
|
from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException
|
||||||
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
|
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
|
||||||
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
||||||
@ -91,7 +91,7 @@ class KubernetesController(BaseController):
|
|||||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||||
raise ControllerException(str(exc)) from exc
|
raise ControllerException(str(exc)) from exc
|
||||||
|
|
||||||
def up_with_logs(self) -> list[LogEvent]:
|
def up_with_logs(self) -> list[str]:
|
||||||
try:
|
try:
|
||||||
all_logs = []
|
all_logs = []
|
||||||
for reconcile_key in self.reconcile_order:
|
for reconcile_key in self.reconcile_order:
|
||||||
@ -104,9 +104,7 @@ class KubernetesController(BaseController):
|
|||||||
continue
|
continue
|
||||||
reconciler = reconciler_cls(self)
|
reconciler = reconciler_cls(self)
|
||||||
reconciler.up()
|
reconciler.up()
|
||||||
for log in logs:
|
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
|
||||||
log.logger = reconcile_key.title()
|
|
||||||
all_logs.extend(logs)
|
|
||||||
return all_logs
|
return all_logs
|
||||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||||
raise ControllerException(str(exc)) from exc
|
raise ControllerException(str(exc)) from exc
|
||||||
@ -124,7 +122,7 @@ class KubernetesController(BaseController):
|
|||||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||||
raise ControllerException(str(exc)) from exc
|
raise ControllerException(str(exc)) from exc
|
||||||
|
|
||||||
def down_with_logs(self) -> list[LogEvent]:
|
def down_with_logs(self) -> list[str]:
|
||||||
try:
|
try:
|
||||||
all_logs = []
|
all_logs = []
|
||||||
for reconcile_key in self.reconcile_order:
|
for reconcile_key in self.reconcile_order:
|
||||||
@ -137,9 +135,7 @@ class KubernetesController(BaseController):
|
|||||||
continue
|
continue
|
||||||
reconciler = reconciler_cls(self)
|
reconciler = reconciler_cls(self)
|
||||||
reconciler.down()
|
reconciler.down()
|
||||||
for log in logs:
|
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
|
||||||
log.logger = reconcile_key.title()
|
|
||||||
all_logs.extend(logs)
|
|
||||||
return all_logs
|
return all_logs
|
||||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||||
raise ControllerException(str(exc)) from exc
|
raise ControllerException(str(exc)) from exc
|
||||||
|
|||||||
@ -149,8 +149,10 @@ def outpost_controller(
|
|||||||
if not controller_type:
|
if not controller_type:
|
||||||
return
|
return
|
||||||
with controller_type(outpost, outpost.service_connection) as controller:
|
with controller_type(outpost, outpost.service_connection) as controller:
|
||||||
LOGGER.debug("---------------Outpost Controller logs starting----------------")
|
|
||||||
logs = getattr(controller, f"{action}_with_logs")()
|
logs = getattr(controller, f"{action}_with_logs")()
|
||||||
|
LOGGER.debug("---------------Outpost Controller logs starting----------------")
|
||||||
|
for log in logs:
|
||||||
|
LOGGER.debug(log)
|
||||||
LOGGER.debug("-----------------Outpost Controller logs end-------------------")
|
LOGGER.debug("-----------------Outpost Controller logs end-------------------")
|
||||||
except (ControllerException, ServiceConnectionInvalid) as exc:
|
except (ControllerException, ServiceConnectionInvalid) as exc:
|
||||||
self.set_error(exc)
|
self.set_error(exc)
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
"""Serializer for policy execution"""
|
"""Serializer for policy execution"""
|
||||||
|
|
||||||
from rest_framework.fields import BooleanField, CharField, ListField
|
from rest_framework.fields import BooleanField, CharField, DictField, ListField
|
||||||
from rest_framework.relations import PrimaryKeyRelatedField
|
from rest_framework.relations import PrimaryKeyRelatedField
|
||||||
|
|
||||||
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.events.logs import LogEventSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class PolicyTestSerializer(PassiveSerializer):
|
class PolicyTestSerializer(PassiveSerializer):
|
||||||
@ -20,4 +19,4 @@ class PolicyTestResultSerializer(PassiveSerializer):
|
|||||||
|
|
||||||
passing = BooleanField()
|
passing = BooleanField()
|
||||||
messages = ListField(child=CharField(), read_only=True)
|
messages = ListField(child=CharField(), read_only=True)
|
||||||
log_messages = LogEventSerializer(many=True, read_only=True)
|
log_messages = ListField(child=DictField(), read_only=True)
|
||||||
|
|||||||
@ -11,11 +11,12 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
from structlog.testing import capture_logs
|
||||||
|
|
||||||
from authentik.core.api.applications import user_app_cache_key
|
from authentik.core.api.applications import user_app_cache_key
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import CacheSerializer, MetaNameSerializer, TypeCreateSerializer
|
from authentik.core.api.utils import CacheSerializer, MetaNameSerializer, TypeCreateSerializer
|
||||||
from authentik.events.logs import LogEventSerializer, capture_logs
|
from authentik.events.utils import sanitize_dict
|
||||||
from authentik.lib.utils.reflection import all_subclasses
|
from authentik.lib.utils.reflection import all_subclasses
|
||||||
from authentik.policies.api.exec import PolicyTestResultSerializer, PolicyTestSerializer
|
from authentik.policies.api.exec import PolicyTestResultSerializer, PolicyTestSerializer
|
||||||
from authentik.policies.models import Policy, PolicyBinding
|
from authentik.policies.models import Policy, PolicyBinding
|
||||||
@ -165,9 +166,9 @@ class PolicyViewSet(
|
|||||||
result = proc.execute()
|
result = proc.execute()
|
||||||
log_messages = []
|
log_messages = []
|
||||||
for log in logs:
|
for log in logs:
|
||||||
if log.attributes.get("process", "") == "PolicyProcess":
|
if log.get("process", "") == "PolicyProcess":
|
||||||
continue
|
continue
|
||||||
log_messages.append(LogEventSerializer(log).data)
|
log_messages.append(sanitize_dict(log))
|
||||||
result.log_messages = log_messages
|
result.log_messages = log_messages
|
||||||
response = PolicyTestResultSerializer(result)
|
response = PolicyTestResultSerializer(result)
|
||||||
return Response(response.data)
|
return Response(response.data)
|
||||||
|
|||||||
@ -13,7 +13,6 @@ from authentik.events.context_processors.base import get_context_processors
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.events.logs import LogEvent
|
|
||||||
from authentik.policies.models import PolicyBinding
|
from authentik.policies.models import PolicyBinding
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
@ -75,7 +74,7 @@ class PolicyResult:
|
|||||||
source_binding: PolicyBinding | None
|
source_binding: PolicyBinding | None
|
||||||
source_results: list[PolicyResult] | None
|
source_results: list[PolicyResult] | None
|
||||||
|
|
||||||
log_messages: list[LogEvent] | None
|
log_messages: list[dict] | None
|
||||||
|
|
||||||
def __init__(self, passing: bool, *messages: str):
|
def __init__(self, passing: bool, *messages: str):
|
||||||
self.passing = passing
|
self.passing = passing
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class OAuthDeviceCodeFinishChallengeResponse(ChallengeResponse):
|
|||||||
|
|
||||||
|
|
||||||
class OAuthDeviceCodeFinishStage(ChallengeStageView):
|
class OAuthDeviceCodeFinishStage(ChallengeStageView):
|
||||||
"""Stage to finish the OAuth device code flow"""
|
"""Stage show at the end of a device flow"""
|
||||||
|
|
||||||
response_class = OAuthDeviceCodeFinishChallengeResponse
|
response_class = OAuthDeviceCodeFinishChallengeResponse
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ErrorDetail
|
||||||
from rest_framework.fields import CharField, IntegerField
|
from rest_framework.fields import CharField, IntegerField
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
@ -57,7 +57,6 @@ def validate_code(code: int, request: HttpRequest) -> HttpResponse | None:
|
|||||||
scope_descriptions = UserInfoView().get_scope_descriptions(token.scope, token.provider)
|
scope_descriptions = UserInfoView().get_scope_descriptions(token.scope, token.provider)
|
||||||
planner = FlowPlanner(token.provider.authorization_flow)
|
planner = FlowPlanner(token.provider.authorization_flow)
|
||||||
planner.allow_empty_flows = True
|
planner.allow_empty_flows = True
|
||||||
planner.use_cache = False
|
|
||||||
try:
|
try:
|
||||||
plan = planner.plan(
|
plan = planner.plan(
|
||||||
request,
|
request,
|
||||||
@ -129,13 +128,6 @@ class OAuthDeviceCodeChallengeResponse(ChallengeResponse):
|
|||||||
code = IntegerField()
|
code = IntegerField()
|
||||||
component = CharField(default="ak-provider-oauth2-device-code")
|
component = CharField(default="ak-provider-oauth2-device-code")
|
||||||
|
|
||||||
def validate_code(self, code: int) -> HttpResponse | None:
|
|
||||||
"""Validate code and save the returned http response"""
|
|
||||||
response = validate_code(code, self.stage.request)
|
|
||||||
if not response:
|
|
||||||
raise ValidationError("Invalid code", "invalid")
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class OAuthDeviceCodeStage(ChallengeStageView):
|
class OAuthDeviceCodeStage(ChallengeStageView):
|
||||||
"""Flow challenge for users to enter device codes"""
|
"""Flow challenge for users to enter device codes"""
|
||||||
@ -151,4 +143,12 @@ class OAuthDeviceCodeStage(ChallengeStageView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
||||||
return response.validated_data["code"]
|
code = response.validated_data["code"]
|
||||||
|
validation = validate_code(code, self.request)
|
||||||
|
if not validation:
|
||||||
|
response._errors.setdefault("code", [])
|
||||||
|
response._errors["code"].append(ErrorDetail(_("Invalid code"), "invalid"))
|
||||||
|
return self.challenge_invalid(response)
|
||||||
|
# Run cancel to cleanup the current flow
|
||||||
|
self.executor.cancel()
|
||||||
|
return validation
|
||||||
|
|||||||
@ -56,10 +56,7 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
|||||||
proxy_provider: ProxyProvider
|
proxy_provider: ProxyProvider
|
||||||
external_host_name = urlparse(proxy_provider.external_host)
|
external_host_name = urlparse(proxy_provider.external_host)
|
||||||
expected_hosts.append(external_host_name.hostname)
|
expected_hosts.append(external_host_name.hostname)
|
||||||
if (
|
if external_host_name.scheme == "https":
|
||||||
external_host_name.scheme == "https"
|
|
||||||
and self.controller.outpost.config.kubernetes_ingress_secret_name
|
|
||||||
):
|
|
||||||
expected_hosts_tls.append(external_host_name.hostname)
|
expected_hosts_tls.append(external_host_name.hostname)
|
||||||
expected_hosts.sort()
|
expected_hosts.sort()
|
||||||
expected_hosts_tls.sort()
|
expected_hosts_tls.sort()
|
||||||
@ -119,10 +116,7 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
|||||||
):
|
):
|
||||||
proxy_provider: ProxyProvider
|
proxy_provider: ProxyProvider
|
||||||
external_host_name = urlparse(proxy_provider.external_host)
|
external_host_name = urlparse(proxy_provider.external_host)
|
||||||
if (
|
if external_host_name.scheme == "https":
|
||||||
external_host_name.scheme == "https"
|
|
||||||
and self.controller.outpost.config.kubernetes_ingress_secret_name
|
|
||||||
):
|
|
||||||
tls_hosts.append(external_host_name.hostname)
|
tls_hosts.append(external_host_name.hostname)
|
||||||
if proxy_provider.mode in [
|
if proxy_provider.mode in [
|
||||||
ProxyMode.FORWARD_SINGLE,
|
ProxyMode.FORWARD_SINGLE,
|
||||||
@ -166,15 +160,13 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
|||||||
rules.append(rule)
|
rules.append(rule)
|
||||||
tls_config = None
|
tls_config = None
|
||||||
if tls_hosts:
|
if tls_hosts:
|
||||||
tls_config = [
|
tls_config = V1IngressTLS(
|
||||||
V1IngressTLS(
|
hosts=tls_hosts,
|
||||||
hosts=tls_hosts,
|
secret_name=self.controller.outpost.config.kubernetes_ingress_secret_name,
|
||||||
secret_name=self.controller.outpost.config.kubernetes_ingress_secret_name,
|
)
|
||||||
)
|
|
||||||
]
|
|
||||||
spec = V1IngressSpec(
|
spec = V1IngressSpec(
|
||||||
rules=rules,
|
rules=rules,
|
||||||
tls=tls_config,
|
tls=[tls_config],
|
||||||
)
|
)
|
||||||
if self.controller.outpost.config.kubernetes_ingress_class_name:
|
if self.controller.outpost.config.kubernetes_ingress_class_name:
|
||||||
spec.ingress_class_name = self.controller.outpost.config.kubernetes_ingress_class_name
|
spec.ingress_class_name = self.controller.outpost.config.kubernetes_ingress_class_name
|
||||||
|
|||||||
@ -196,10 +196,8 @@ if CONFIG.get_bool("redis.tls", False):
|
|||||||
_redis_protocol_prefix = "rediss://"
|
_redis_protocol_prefix = "rediss://"
|
||||||
_redis_celery_tls_requirements = f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
|
_redis_celery_tls_requirements = f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
|
||||||
_redis_url = (
|
_redis_url = (
|
||||||
f"{_redis_protocol_prefix}"
|
f"{_redis_protocol_prefix}:"
|
||||||
f"{quote_plus(CONFIG.get('redis.username'))}:"
|
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
|
||||||
f"{quote_plus(CONFIG.get('redis.password'))}@"
|
|
||||||
f"{quote_plus(CONFIG.get('redis.host'))}:"
|
|
||||||
f"{CONFIG.get_int('redis.port')}"
|
f"{CONFIG.get_int('redis.port')}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,6 @@ class LDAPSourceSerializer(SourceSerializer):
|
|||||||
"group_object_filter",
|
"group_object_filter",
|
||||||
"group_membership_field",
|
"group_membership_field",
|
||||||
"object_uniqueness_field",
|
"object_uniqueness_field",
|
||||||
"password_login_update_internal_password",
|
|
||||||
"sync_users",
|
"sync_users",
|
||||||
"sync_users_password",
|
"sync_users_password",
|
||||||
"sync_groups",
|
"sync_groups",
|
||||||
@ -119,7 +118,6 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
|||||||
"group_object_filter",
|
"group_object_filter",
|
||||||
"group_membership_field",
|
"group_membership_field",
|
||||||
"object_uniqueness_field",
|
"object_uniqueness_field",
|
||||||
"password_login_update_internal_password",
|
|
||||||
"sync_users",
|
"sync_users",
|
||||||
"sync_users_password",
|
"sync_users_password",
|
||||||
"sync_groups",
|
"sync_groups",
|
||||||
|
|||||||
@ -41,11 +41,10 @@ class LDAPBackend(InbuiltBackend):
|
|||||||
# or has a password, but couldn't be authenticated by ModelBackend.
|
# or has a password, but couldn't be authenticated by ModelBackend.
|
||||||
# This means we check with a bind to see if the LDAP password has changed
|
# This means we check with a bind to see if the LDAP password has changed
|
||||||
if self.auth_user_by_bind(source, user, password):
|
if self.auth_user_by_bind(source, user, password):
|
||||||
if source.password_login_update_internal_password:
|
# Password given successfully binds to LDAP, so we save it in our Database
|
||||||
# Password given successfully binds to LDAP, so we save it in our Database
|
LOGGER.debug("Updating user's password in DB", user=user)
|
||||||
LOGGER.debug("Updating user's password in DB", user=user)
|
user.set_password(password, signal=False)
|
||||||
user.set_password(password, signal=False)
|
user.save()
|
||||||
user.save()
|
|
||||||
return user
|
return user
|
||||||
# Password doesn't match
|
# Password doesn't match
|
||||||
LOGGER.debug("Failed to bind, password invalid")
|
LOGGER.debug("Failed to bind, password invalid")
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
# Generated by Django 5.0.1 on 2024-01-31 18:41
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("authentik_sources_ldap", "0003_ldapsource_client_certificate_ldapsource_sni_and_more"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="ldapsource",
|
|
||||||
name="password_login_update_internal_password",
|
|
||||||
field=models.BooleanField(
|
|
||||||
default=True,
|
|
||||||
help_text="Update internal authentik password when login succeeds with LDAP",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="ldapsource",
|
|
||||||
name="password_login_update_internal_password",
|
|
||||||
field=models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Update internal authentik password when login succeeds with LDAP",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -98,11 +98,6 @@ class LDAPSource(Source):
|
|||||||
help_text=_("Property mappings used for group creation/updating."),
|
help_text=_("Property mappings used for group creation/updating."),
|
||||||
)
|
)
|
||||||
|
|
||||||
password_login_update_internal_password = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text=_("Update internal authentik password when login succeeds with LDAP"),
|
|
||||||
)
|
|
||||||
|
|
||||||
sync_users = models.BooleanField(default=True)
|
sync_users = models.BooleanField(default=True)
|
||||||
sync_users_password = models.BooleanField(
|
sync_users_password = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
|
|||||||
@ -47,7 +47,7 @@ class SourceType:
|
|||||||
def login_challenge(self, source: OAuthSource, request: HttpRequest) -> Challenge:
|
def login_challenge(self, source: OAuthSource, request: HttpRequest) -> Challenge:
|
||||||
"""Allow types to return custom challenges"""
|
"""Allow types to return custom challenges"""
|
||||||
return RedirectChallenge(
|
return RedirectChallenge(
|
||||||
data={
|
instance={
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
"type": ChallengeTypes.REDIRECT.value,
|
||||||
"to": reverse(
|
"to": reverse(
|
||||||
"authentik_sources_oauth:oauth-client-login",
|
"authentik_sources_oauth:oauth-client-login",
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class OAuthCallback(OAuthClientMixin, View):
|
|||||||
raw_profile=exc.doc,
|
raw_profile=exc.doc,
|
||||||
).from_http(self.request)
|
).from_http(self.request)
|
||||||
return self.handle_login_failure("Could not retrieve profile.")
|
return self.handle_login_failure("Could not retrieve profile.")
|
||||||
identifier = self.get_user_id(info=raw_info)
|
identifier = self.get_user_id(raw_info)
|
||||||
if identifier is None:
|
if identifier is None:
|
||||||
return self.handle_login_failure("Could not determine id.")
|
return self.handle_login_failure("Could not determine id.")
|
||||||
# Get or create access record
|
# Get or create access record
|
||||||
@ -67,7 +67,6 @@ class OAuthCallback(OAuthClientMixin, View):
|
|||||||
)
|
)
|
||||||
sfm.policy_context = {"oauth_userinfo": raw_info}
|
sfm.policy_context = {"oauth_userinfo": raw_info}
|
||||||
return sfm.get_flow(
|
return sfm.get_flow(
|
||||||
raw_info=raw_info,
|
|
||||||
access_token=self.token.get("access_token"),
|
access_token=self.token.get("access_token"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -117,7 +116,6 @@ class OAuthSourceFlowManager(SourceFlowManager):
|
|||||||
self,
|
self,
|
||||||
connection: UserOAuthSourceConnection,
|
connection: UserOAuthSourceConnection,
|
||||||
access_token: str | None = None,
|
access_token: str | None = None,
|
||||||
**_,
|
|
||||||
) -> UserOAuthSourceConnection:
|
) -> UserOAuthSourceConnection:
|
||||||
"""Set the access_token on the connection"""
|
"""Set the access_token on the connection"""
|
||||||
connection.access_token = access_token
|
connection.access_token = access_token
|
||||||
|
|||||||
@ -190,7 +190,7 @@ class SAMLSource(Source):
|
|||||||
def ui_login_button(self, request: HttpRequest) -> UILoginButton:
|
def ui_login_button(self, request: HttpRequest) -> UILoginButton:
|
||||||
return UILoginButton(
|
return UILoginButton(
|
||||||
challenge=RedirectChallenge(
|
challenge=RedirectChallenge(
|
||||||
data={
|
instance={
|
||||||
"type": ChallengeTypes.REDIRECT.value,
|
"type": ChallengeTypes.REDIRECT.value,
|
||||||
"to": reverse(
|
"to": reverse(
|
||||||
"authentik_sources_saml:login",
|
"authentik_sources_saml:login",
|
||||||
|
|||||||
@ -234,14 +234,12 @@ class ResponseProcessor:
|
|||||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
|
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
|
||||||
return self._handle_name_id_transient()
|
return self._handle_name_id_transient()
|
||||||
|
|
||||||
flow_manager = SAMLSourceFlowManager(
|
return SAMLSourceFlowManager(
|
||||||
self._source,
|
self._source,
|
||||||
self._http_request,
|
self._http_request,
|
||||||
name_id.text,
|
name_id.text,
|
||||||
delete_none_values(self.get_attributes()),
|
delete_none_values(self.get_attributes()),
|
||||||
)
|
)
|
||||||
flow_manager.policy_context["saml_response"] = self._root
|
|
||||||
return flow_manager
|
|
||||||
|
|
||||||
|
|
||||||
class SAMLSourceFlowManager(SourceFlowManager):
|
class SAMLSourceFlowManager(SourceFlowManager):
|
||||||
|
|||||||
@ -120,9 +120,7 @@ def validate_challenge_code(code: str, stage_view: StageView, user: User) -> Dev
|
|||||||
stage=stage_view.executor.current_stage,
|
stage=stage_view.executor.current_stage,
|
||||||
device_class=DeviceClasses.TOTP.value,
|
device_class=DeviceClasses.TOTP.value,
|
||||||
)
|
)
|
||||||
raise ValidationError(
|
raise ValidationError(_("Invalid Token"))
|
||||||
_("Invalid Token. Please ensure the time on your device is accurate and try again.")
|
|
||||||
)
|
|
||||||
return device
|
return device
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,6 @@ class DummyChallenge(Challenge):
|
|||||||
"""Dummy challenge"""
|
"""Dummy challenge"""
|
||||||
|
|
||||||
component = CharField(default="ak-stage-dummy")
|
component = CharField(default="ak-stage-dummy")
|
||||||
name = CharField()
|
|
||||||
|
|
||||||
|
|
||||||
class DummyChallengeResponse(ChallengeResponse):
|
class DummyChallengeResponse(ChallengeResponse):
|
||||||
@ -36,6 +35,5 @@ class DummyStageView(ChallengeStageView):
|
|||||||
data={
|
data={
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
"title": self.executor.current_stage.name,
|
"title": self.executor.current_stage.name,
|
||||||
"name": self.executor.current_stage.name,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from django.db.models import Q
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
|
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
|
||||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField, ListField
|
from rest_framework.fields import BooleanField, CharField, DictField, ListField
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
from sentry_sdk.hub import Hub
|
from sentry_sdk.hub import Hub
|
||||||
|
|
||||||
@ -66,7 +66,6 @@ class IdentificationChallenge(Challenge):
|
|||||||
user_fields = ListField(child=CharField(), allow_empty=True, allow_null=True)
|
user_fields = ListField(child=CharField(), allow_empty=True, allow_null=True)
|
||||||
password_fields = BooleanField()
|
password_fields = BooleanField()
|
||||||
application_pre = CharField(required=False)
|
application_pre = CharField(required=False)
|
||||||
flow_designation = ChoiceField(FlowDesignation.choices)
|
|
||||||
|
|
||||||
enroll_url = CharField(required=False)
|
enroll_url = CharField(required=False)
|
||||||
recovery_url = CharField(required=False)
|
recovery_url = CharField(required=False)
|
||||||
@ -195,12 +194,11 @@ class IdentificationStageView(ChallengeStageView):
|
|||||||
challenge = IdentificationChallenge(
|
challenge = IdentificationChallenge(
|
||||||
data={
|
data={
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
"component": "ak-stage-identification",
|
|
||||||
"primary_action": self.get_primary_action(),
|
"primary_action": self.get_primary_action(),
|
||||||
|
"component": "ak-stage-identification",
|
||||||
"user_fields": current_stage.user_fields,
|
"user_fields": current_stage.user_fields,
|
||||||
"password_fields": bool(current_stage.password_stage),
|
"password_fields": bool(current_stage.password_stage),
|
||||||
"show_source_labels": current_stage.show_source_labels,
|
"show_source_labels": current_stage.show_source_labels,
|
||||||
"flow_designation": self.executor.flow.designation,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# If the user has been redirected to us whilst trying to access an
|
# If the user has been redirected to us whilst trying to access an
|
||||||
@ -239,9 +237,7 @@ class IdentificationStageView(ChallengeStageView):
|
|||||||
ui_login_button = source.ui_login_button(self.request)
|
ui_login_button = source.ui_login_button(self.request)
|
||||||
if ui_login_button:
|
if ui_login_button:
|
||||||
button = asdict(ui_login_button)
|
button = asdict(ui_login_button)
|
||||||
source_challenge = ui_login_button.challenge
|
button["challenge"] = ui_login_button.challenge.data
|
||||||
source_challenge.is_valid()
|
|
||||||
button["challenge"] = source_challenge.data
|
|
||||||
ui_sources.append(button)
|
ui_sources.append(button)
|
||||||
challenge.initial_data["sources"] = ui_sources
|
challenge.initial_data["sources"] = ui_sources
|
||||||
return challenge
|
return challenge
|
||||||
|
|||||||
@ -12,7 +12,6 @@ from rest_framework.exceptions import ValidationError
|
|||||||
from authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER
|
from authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER
|
||||||
from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection, UserTypes
|
from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection, UserTypes
|
||||||
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
|
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
|
||||||
from authentik.events.utils import sanitize_item
|
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.stage import StageView
|
from authentik.flows.stage import StageView
|
||||||
from authentik.flows.views.executor import FlowExecutorView
|
from authentik.flows.views.executor import FlowExecutorView
|
||||||
@ -48,7 +47,7 @@ class UserWriteStageView(StageView):
|
|||||||
# this is just a sanity check to ensure that is removed
|
# this is just a sanity check to ensure that is removed
|
||||||
if parts[0] == "attributes":
|
if parts[0] == "attributes":
|
||||||
parts = parts[1:]
|
parts = parts[1:]
|
||||||
set_path_in_dict(user.attributes, ".".join(parts), sanitize_item(value))
|
set_path_in_dict(user.attributes, ".".join(parts), value)
|
||||||
|
|
||||||
def ensure_user(self) -> tuple[User | None, bool]:
|
def ensure_user(self) -> tuple[User | None, bool]:
|
||||||
"""Ensure a user exists"""
|
"""Ensure a user exists"""
|
||||||
|
|||||||
@ -2594,43 +2594,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"model",
|
|
||||||
"identifiers"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"model": {
|
|
||||||
"const": "authentik_stages_source.sourcestage"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"absent",
|
|
||||||
"present",
|
|
||||||
"created",
|
|
||||||
"must_created"
|
|
||||||
],
|
|
||||||
"default": "present"
|
|
||||||
},
|
|
||||||
"conditions": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"attrs": {
|
|
||||||
"$ref": "#/$defs/model_authentik_stages_source.sourcestage"
|
|
||||||
},
|
|
||||||
"identifiers": {
|
|
||||||
"$ref": "#/$defs/model_authentik_stages_source.sourcestage"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -3294,7 +3257,6 @@
|
|||||||
"authentik.enterprise",
|
"authentik.enterprise",
|
||||||
"authentik.enterprise.audit",
|
"authentik.enterprise.audit",
|
||||||
"authentik.enterprise.providers.rac",
|
"authentik.enterprise.providers.rac",
|
||||||
"authentik.enterprise.stages.source",
|
|
||||||
"authentik.events"
|
"authentik.events"
|
||||||
],
|
],
|
||||||
"title": "App",
|
"title": "App",
|
||||||
@ -3376,7 +3338,6 @@
|
|||||||
"authentik_providers_rac.racprovider",
|
"authentik_providers_rac.racprovider",
|
||||||
"authentik_providers_rac.endpoint",
|
"authentik_providers_rac.endpoint",
|
||||||
"authentik_providers_rac.racpropertymapping",
|
"authentik_providers_rac.racpropertymapping",
|
||||||
"authentik_stages_source.sourcestage",
|
|
||||||
"authentik_events.event",
|
"authentik_events.event",
|
||||||
"authentik_events.notificationtransport",
|
"authentik_events.notificationtransport",
|
||||||
"authentik_events.notification",
|
"authentik_events.notification",
|
||||||
@ -4347,11 +4308,6 @@
|
|||||||
"title": "Object uniqueness field",
|
"title": "Object uniqueness field",
|
||||||
"description": "Field which contains a unique Identifier."
|
"description": "Field which contains a unique Identifier."
|
||||||
},
|
},
|
||||||
"password_login_update_internal_password": {
|
|
||||||
"type": "boolean",
|
|
||||||
"title": "Password login update internal password",
|
|
||||||
"description": "Update internal authentik password when login succeeds with LDAP"
|
|
||||||
},
|
|
||||||
"sync_users": {
|
"sync_users": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"title": "Sync users"
|
"title": "Sync users"
|
||||||
@ -8062,109 +8018,6 @@
|
|||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
},
|
},
|
||||||
"model_authentik_stages_source.sourcestage": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
|
||||||
"minLength": 1,
|
|
||||||
"title": "Name"
|
|
||||||
},
|
|
||||||
"flow_set": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
|
||||||
"minLength": 1,
|
|
||||||
"title": "Name"
|
|
||||||
},
|
|
||||||
"slug": {
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 50,
|
|
||||||
"minLength": 1,
|
|
||||||
"pattern": "^[-a-zA-Z0-9_]+$",
|
|
||||||
"title": "Slug",
|
|
||||||
"description": "Visible in the URL."
|
|
||||||
},
|
|
||||||
"title": {
|
|
||||||
"type": "string",
|
|
||||||
"minLength": 1,
|
|
||||||
"title": "Title",
|
|
||||||
"description": "Shown as the Title in Flow pages."
|
|
||||||
},
|
|
||||||
"designation": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"authentication",
|
|
||||||
"authorization",
|
|
||||||
"invalidation",
|
|
||||||
"enrollment",
|
|
||||||
"unenrollment",
|
|
||||||
"recovery",
|
|
||||||
"stage_configuration"
|
|
||||||
],
|
|
||||||
"title": "Designation",
|
|
||||||
"description": "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
|
|
||||||
},
|
|
||||||
"policy_engine_mode": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"all",
|
|
||||||
"any"
|
|
||||||
],
|
|
||||||
"title": "Policy engine mode"
|
|
||||||
},
|
|
||||||
"compatibility_mode": {
|
|
||||||
"type": "boolean",
|
|
||||||
"title": "Compatibility mode",
|
|
||||||
"description": "Enable compatibility mode, increases compatibility with password managers on mobile devices."
|
|
||||||
},
|
|
||||||
"layout": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"stacked",
|
|
||||||
"content_left",
|
|
||||||
"content_right",
|
|
||||||
"sidebar_left",
|
|
||||||
"sidebar_right"
|
|
||||||
],
|
|
||||||
"title": "Layout"
|
|
||||||
},
|
|
||||||
"denied_action": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"message_continue",
|
|
||||||
"message",
|
|
||||||
"continue"
|
|
||||||
],
|
|
||||||
"title": "Denied action",
|
|
||||||
"description": "Configure what should happen when a flow denies access to a user."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"name",
|
|
||||||
"slug",
|
|
||||||
"title",
|
|
||||||
"designation"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"title": "Flow set"
|
|
||||||
},
|
|
||||||
"source": {
|
|
||||||
"type": "integer",
|
|
||||||
"title": "Source"
|
|
||||||
},
|
|
||||||
"resume_timeout": {
|
|
||||||
"type": "string",
|
|
||||||
"minLength": 1,
|
|
||||||
"title": "Resume timeout",
|
|
||||||
"description": "Amount of time a user can take to return from the source to continue the flow (Format: hours=-1;minutes=-2;seconds=-3)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": []
|
|
||||||
},
|
|
||||||
"model_authentik_events.event": {
|
"model_authentik_events.event": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -30,7 +30,7 @@ require (
|
|||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/wwt/guac v1.3.2
|
github.com/wwt/guac v1.3.2
|
||||||
goauthentik.io/api/v3 v3.2024022.7
|
goauthentik.io/api/v3 v3.2024022.1
|
||||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||||
golang.org/x/oauth2 v0.18.0
|
golang.org/x/oauth2 v0.18.0
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/sync v0.6.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -280,8 +280,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
|||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||||
goauthentik.io/api/v3 v3.2024022.7 h1:VR9OmcZvTzPSjit2Dx2EoHrLc9v9XRyjPXNpnGISWWM=
|
goauthentik.io/api/v3 v3.2024022.1 h1:ydYi3X/OSnu4LumUN+oCe6vvGDXil1Xn186hC9FQb4Q=
|
||||||
goauthentik.io/api/v3 v3.2024022.7/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
goauthentik.io/api/v3 v3.2024022.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
|||||||
@ -10,17 +10,12 @@ const CodePasswordSeparator = ";"
|
|||||||
|
|
||||||
var alphaNum = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
|
var alphaNum = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
|
||||||
|
|
||||||
// Sets the secret answers for the flow executor for protocols that only support username/password
|
// CheckPasswordInlineMFA For protocols that only support username/password, check if the password
|
||||||
// according to used options
|
// contains the TOTP code
|
||||||
func (fe *FlowExecutor) SetSecrets(password string, mfaCodeBased bool) {
|
func (fe *FlowExecutor) CheckPasswordInlineMFA() {
|
||||||
if fe.Answers[StageAuthenticatorValidate] != "" || fe.Answers[StagePassword] != "" {
|
password := fe.Answers[StagePassword]
|
||||||
return
|
// We already have an authenticator answer
|
||||||
}
|
if fe.Answers[StageAuthenticatorValidate] != "" {
|
||||||
fe.Answers[StagePassword] = password
|
|
||||||
if !mfaCodeBased {
|
|
||||||
// If code-based MFA is disabled StageAuthenticatorValidate answer is set to password.
|
|
||||||
// This allows flows with a mfa stage only.
|
|
||||||
fe.Answers[StageAuthenticatorValidate] = password
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// password doesn't contain the separator
|
// password doesn't contain the separator
|
||||||
|
|||||||
@ -23,7 +23,10 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
|||||||
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
||||||
|
|
||||||
fe.Answers[flow.StageIdentification] = username
|
fe.Answers[flow.StageIdentification] = username
|
||||||
fe.SetSecrets(req.BindPW, db.si.GetMFASupport())
|
fe.Answers[flow.StagePassword] = req.BindPW
|
||||||
|
if db.si.GetMFASupport() {
|
||||||
|
fe.CheckPasswordInlineMFA()
|
||||||
|
}
|
||||||
|
|
||||||
passed, err := fe.Execute()
|
passed, err := fe.Execute()
|
||||||
flags := flags.UserFlags{
|
flags := flags.UserFlags{
|
||||||
|
|||||||
@ -35,7 +35,7 @@ type ProviderInstance struct {
|
|||||||
cert *tls.Certificate
|
cert *tls.Certificate
|
||||||
certUUID string
|
certUUID string
|
||||||
outpostName string
|
outpostName string
|
||||||
providerPk int32
|
outpostPk int32
|
||||||
searchAllowedGroups []*strfmt.UUID
|
searchAllowedGroups []*strfmt.UUID
|
||||||
boundUsersMutex *sync.RWMutex
|
boundUsersMutex *sync.RWMutex
|
||||||
boundUsers map[string]*flags.UserFlags
|
boundUsers map[string]*flags.UserFlags
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
func (ls *LDAPServer) getCurrentProvider(pk int32) *ProviderInstance {
|
func (ls *LDAPServer) getCurrentProvider(pk int32) *ProviderInstance {
|
||||||
for _, p := range ls.providers {
|
for _, p := range ls.providers {
|
||||||
if p.providerPk == pk {
|
if p.outpostPk == pk {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ func (ls *LDAPServer) Refresh() error {
|
|||||||
gidStartNumber: provider.GetGidStartNumber(),
|
gidStartNumber: provider.GetGidStartNumber(),
|
||||||
mfaSupport: provider.GetMfaSupport(),
|
mfaSupport: provider.GetMfaSupport(),
|
||||||
outpostName: ls.ac.Outpost.Name,
|
outpostName: ls.ac.Outpost.Name,
|
||||||
providerPk: provider.Pk,
|
outpostPk: provider.Pk,
|
||||||
}
|
}
|
||||||
if kp := provider.Certificate.Get(); kp != nil {
|
if kp := provider.Certificate.Get(); kp != nil {
|
||||||
err := ls.cs.AddKeypair(*kp)
|
err := ls.cs.AddKeypair(*kp)
|
||||||
|
|||||||
@ -56,7 +56,7 @@ func TestProxy_Redirect_Subdirectory(t *testing.T) {
|
|||||||
loc, _ := rr.Result().Location()
|
loc, _ := rr.Result().Location()
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
t,
|
t,
|
||||||
"https://ext.t.goauthentik.io/subdir/outpost.goauthentik.io/start?rd=https%3A%2F%2Fext.t.goauthentik.io%2Fsubdir%2Ffoo",
|
"https://ext.t.goauthentik.io/subdir/outpost.goauthentik.io/start?rd=https%3A%2F%2Fext.t.goauthentik.io%2Ffoo",
|
||||||
loc.String(),
|
loc.String(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,8 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
|
|||||||
}
|
}
|
||||||
if a.isEmbedded {
|
if a.isEmbedded {
|
||||||
client := redis.NewClient(&redis.Options{
|
client := redis.NewClient(&redis.Options{
|
||||||
Addr: fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
|
Addr: fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
|
||||||
Username: config.Get().Redis.Username,
|
// Username: config.Get().Redis.Password,
|
||||||
Password: config.Get().Redis.Password,
|
Password: config.Get().Redis.Password,
|
||||||
DB: config.Get().Redis.DB,
|
DB: config.Get().Redis.DB,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package application
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -10,12 +11,22 @@ import (
|
|||||||
"goauthentik.io/internal/outpost/proxyv2/constants"
|
"goauthentik.io/internal/outpost/proxyv2/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
func urlJoin(originalUrl string, newPath string) string {
|
func urlPathSet(originalUrl string, newPath string) string {
|
||||||
u, err := url.JoinPath(originalUrl, newPath)
|
u, err := url.Parse(originalUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return originalUrl
|
return originalUrl
|
||||||
}
|
}
|
||||||
return u
|
u.Path = newPath
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlJoin(originalUrl string, newPath string) string {
|
||||||
|
u, err := url.Parse(originalUrl)
|
||||||
|
if err != nil {
|
||||||
|
return originalUrl
|
||||||
|
}
|
||||||
|
u.Path = path.Join(u.Path, newPath)
|
||||||
|
return u.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) {
|
func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) {
|
||||||
@ -35,7 +46,7 @@ func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectUrl := urlJoin(a.proxyConfig.ExternalHost, r.URL.Path)
|
redirectUrl := urlPathSet(a.proxyConfig.ExternalHost, r.URL.Path)
|
||||||
|
|
||||||
if a.Mode() == api.PROXYMODE_FORWARD_DOMAIN {
|
if a.Mode() == api.PROXYMODE_FORWARD_DOMAIN {
|
||||||
dom := strings.TrimPrefix(*a.proxyConfig.CookieDomain, ".")
|
dom := strings.TrimPrefix(*a.proxyConfig.CookieDomain, ".")
|
||||||
|
|||||||
@ -6,10 +6,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"goauthentik.io/internal/outpost/ldap/flags"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseCIDRs(raw string) []*net.IPNet {
|
func parseCIDRs(raw string) []*net.IPNet {
|
||||||
@ -31,25 +29,6 @@ func parseCIDRs(raw string) []*net.IPNet {
|
|||||||
return cidrs
|
return cidrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *RadiusServer) getCurrentProvider(pk int32) *ProviderInstance {
|
|
||||||
for _, p := range rs.providers {
|
|
||||||
if p.providerPk == pk {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *RadiusServer) getInvalidationFlow() string {
|
|
||||||
req, _, err := rs.ac.Client.CoreApi.CoreBrandsCurrentRetrieve(context.Background()).Execute()
|
|
||||||
if err != nil {
|
|
||||||
rs.log.WithError(err).Warning("failed to fetch brand config")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
flow := req.GetFlowInvalidation()
|
|
||||||
return flow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *RadiusServer) Refresh() error {
|
func (rs *RadiusServer) Refresh() error {
|
||||||
outposts, _, err := rs.ac.Client.OutpostsApi.OutpostsRadiusList(context.Background()).Execute()
|
outposts, _, err := rs.ac.Client.OutpostsApi.OutpostsRadiusList(context.Background()).Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -58,33 +37,17 @@ func (rs *RadiusServer) Refresh() error {
|
|||||||
if len(outposts.Results) < 1 {
|
if len(outposts.Results) < 1 {
|
||||||
return errors.New("no radius provider defined")
|
return errors.New("no radius provider defined")
|
||||||
}
|
}
|
||||||
invalidationFlow := rs.getInvalidationFlow()
|
|
||||||
providers := make([]*ProviderInstance, len(outposts.Results))
|
providers := make([]*ProviderInstance, len(outposts.Results))
|
||||||
for idx, provider := range outposts.Results {
|
for idx, provider := range outposts.Results {
|
||||||
logger := log.WithField("logger", "authentik.outpost.radius").WithField("provider", provider.Name)
|
logger := log.WithField("logger", "authentik.outpost.radius").WithField("provider", provider.Name)
|
||||||
|
|
||||||
// Get existing instance so we can transfer boundUsers
|
|
||||||
existing := rs.getCurrentProvider(provider.Pk)
|
|
||||||
usersMutex := &sync.RWMutex{}
|
|
||||||
users := make(map[string]*flags.UserFlags)
|
|
||||||
if existing != nil {
|
|
||||||
usersMutex = existing.boundUsersMutex
|
|
||||||
// Shallow copy, no need to lock
|
|
||||||
users = existing.boundUsers
|
|
||||||
}
|
|
||||||
|
|
||||||
providers[idx] = &ProviderInstance{
|
providers[idx] = &ProviderInstance{
|
||||||
SharedSecret: []byte(provider.GetSharedSecret()),
|
SharedSecret: []byte(provider.GetSharedSecret()),
|
||||||
ClientNetworks: parseCIDRs(provider.GetClientNetworks()),
|
ClientNetworks: parseCIDRs(provider.GetClientNetworks()),
|
||||||
MFASupport: provider.GetMfaSupport(),
|
MFASupport: provider.GetMfaSupport(),
|
||||||
appSlug: provider.ApplicationSlug,
|
appSlug: provider.ApplicationSlug,
|
||||||
authenticationFlowSlug: provider.AuthFlowSlug,
|
flowSlug: provider.AuthFlowSlug,
|
||||||
invalidationFlowSlug: invalidationFlow,
|
s: rs,
|
||||||
s: rs,
|
log: logger,
|
||||||
log: logger,
|
|
||||||
providerPk: provider.Pk,
|
|
||||||
boundUsersMutex: usersMutex,
|
|
||||||
boundUsers: users,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rs.providers = providers
|
rs.providers = providers
|
||||||
|
|||||||
@ -4,17 +4,15 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"goauthentik.io/internal/outpost/flow"
|
"goauthentik.io/internal/outpost/flow"
|
||||||
"goauthentik.io/internal/outpost/ldap/flags"
|
|
||||||
"goauthentik.io/internal/outpost/radius/metrics"
|
"goauthentik.io/internal/outpost/radius/metrics"
|
||||||
"layeh.com/radius"
|
"layeh.com/radius"
|
||||||
"layeh.com/radius/rfc2865"
|
"layeh.com/radius/rfc2865"
|
||||||
"layeh.com/radius/rfc2866"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusRequest) {
|
func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusRequest) {
|
||||||
username := rfc2865.UserName_GetString(r.Packet)
|
username := rfc2865.UserName_GetString(r.Packet)
|
||||||
|
|
||||||
fe := flow.NewFlowExecutor(r.Context(), r.pi.authenticationFlowSlug, r.pi.s.ac.Client.GetConfig(), log.Fields{
|
fe := flow.NewFlowExecutor(r.Context(), r.pi.flowSlug, r.pi.s.ac.Client.GetConfig(), log.Fields{
|
||||||
"username": username,
|
"username": username,
|
||||||
"client": r.RemoteAddr(),
|
"client": r.RemoteAddr(),
|
||||||
"requestId": r.ID(),
|
"requestId": r.ID(),
|
||||||
@ -23,7 +21,10 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
|||||||
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
||||||
|
|
||||||
fe.Answers[flow.StageIdentification] = username
|
fe.Answers[flow.StageIdentification] = username
|
||||||
fe.SetSecrets(rfc2865.UserPassword_GetString(r.Packet), r.pi.MFASupport)
|
fe.Answers[flow.StagePassword] = rfc2865.UserPassword_GetString(r.Packet)
|
||||||
|
if r.pi.MFASupport {
|
||||||
|
fe.CheckPasswordInlineMFA()
|
||||||
|
}
|
||||||
|
|
||||||
passed, err := fe.Execute()
|
passed, err := fe.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -66,28 +67,5 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
|||||||
}).Inc()
|
}).Inc()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Get user info to store in context
|
_ = w.Write(r.Response(radius.CodeAccessAccept))
|
||||||
userInfo, _, err := fe.ApiClient().CoreApi.CoreUsersMeRetrieve(r.Context()).Execute()
|
|
||||||
if err != nil {
|
|
||||||
metrics.RequestsRejected.With(prometheus.Labels{
|
|
||||||
"outpost_name": rs.ac.Outpost.Name,
|
|
||||||
"type": "bind",
|
|
||||||
"reason": "user_info_fail",
|
|
||||||
}).Inc()
|
|
||||||
r.Log().WithError(err).Warning("failed to get user info")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response := r.Response(radius.CodeAccessAccept)
|
|
||||||
_ = rfc2866.AcctSessionID_SetString(response, fe.GetSession().String())
|
|
||||||
r.pi.boundUsersMutex.Lock()
|
|
||||||
r.pi.boundUsers[fe.GetSession().String()] = &flags.UserFlags{
|
|
||||||
Session: fe.GetSession(),
|
|
||||||
UserPk: userInfo.Original.Pk,
|
|
||||||
}
|
|
||||||
r.pi.boundUsersMutex.Unlock()
|
|
||||||
err = w.Write(response)
|
|
||||||
if err != nil {
|
|
||||||
r.Log().WithError(err).Warning("failed to write response")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
package radius
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"goauthentik.io/internal/outpost/flow"
|
|
||||||
"goauthentik.io/internal/outpost/ldap/flags"
|
|
||||||
"layeh.com/radius"
|
|
||||||
"layeh.com/radius/rfc2866"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (rs *RadiusServer) Handle_DisconnectRequest(w radius.ResponseWriter, r *RadiusRequest) {
|
|
||||||
session := rfc2866.AcctSessionID_GetString(r.Packet)
|
|
||||||
|
|
||||||
sendFailResponse := func() {
|
|
||||||
failResponse := r.Response(radius.CodeDisconnectACK)
|
|
||||||
err := w.Write(failResponse)
|
|
||||||
if err != nil {
|
|
||||||
r.Log().WithError(err).Warning("failed to write response")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.pi.boundUsersMutex.Lock()
|
|
||||||
var f *flags.UserFlags
|
|
||||||
if ff, ok := r.pi.boundUsers[session]; !ok {
|
|
||||||
r.pi.boundUsersMutex.Unlock()
|
|
||||||
sendFailResponse()
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
f = ff
|
|
||||||
}
|
|
||||||
r.pi.boundUsersMutex.Unlock()
|
|
||||||
|
|
||||||
fe := flow.NewFlowExecutor(r.Context(), r.pi.invalidationFlowSlug, rs.ac.Client.GetConfig(), log.Fields{
|
|
||||||
"client": r.RemoteAddr(),
|
|
||||||
"requestId": r.ID(),
|
|
||||||
})
|
|
||||||
fe.SetSession(f.Session)
|
|
||||||
fe.DelegateClientIP(r.RemoteAddr())
|
|
||||||
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
|
||||||
_, err := fe.Execute()
|
|
||||||
if err != nil {
|
|
||||||
r.log.WithError(err).Warning("failed to logout user")
|
|
||||||
sendFailResponse()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.pi.boundUsersMutex.Lock()
|
|
||||||
delete(r.pi.boundUsers, session)
|
|
||||||
r.pi.boundUsersMutex.Unlock()
|
|
||||||
response := r.Response(radius.CodeDisconnectACK)
|
|
||||||
err = w.Write(response)
|
|
||||||
if err != nil {
|
|
||||||
r.Log().WithError(err).Warning("failed to write response")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -74,12 +74,7 @@ func (rs *RadiusServer) ServeRADIUS(w radius.ResponseWriter, r *radius.Request)
|
|||||||
}
|
}
|
||||||
nr.pi = pi
|
nr.pi = pi
|
||||||
|
|
||||||
switch nr.Code {
|
if nr.Code == radius.CodeAccessRequest {
|
||||||
case radius.CodeAccessRequest:
|
|
||||||
rs.Handle_AccessRequest(w, nr)
|
rs.Handle_AccessRequest(w, nr)
|
||||||
case radius.CodeDisconnectRequest:
|
|
||||||
rs.Handle_DisconnectRequest(w, nr)
|
|
||||||
default:
|
|
||||||
nr.Log().WithField("code", nr.Code.String()).Debug("Unsupported packet code")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,25 +9,20 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"goauthentik.io/internal/config"
|
"goauthentik.io/internal/config"
|
||||||
"goauthentik.io/internal/outpost/ak"
|
"goauthentik.io/internal/outpost/ak"
|
||||||
"goauthentik.io/internal/outpost/ldap/flags"
|
|
||||||
"goauthentik.io/internal/outpost/radius/metrics"
|
"goauthentik.io/internal/outpost/radius/metrics"
|
||||||
|
|
||||||
"layeh.com/radius"
|
"layeh.com/radius"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProviderInstance struct {
|
type ProviderInstance struct {
|
||||||
ClientNetworks []*net.IPNet
|
ClientNetworks []*net.IPNet
|
||||||
SharedSecret []byte
|
SharedSecret []byte
|
||||||
MFASupport bool
|
MFASupport bool
|
||||||
boundUsersMutex *sync.RWMutex
|
|
||||||
boundUsers map[string]*flags.UserFlags
|
|
||||||
providerPk int32
|
|
||||||
|
|
||||||
appSlug string
|
appSlug string
|
||||||
authenticationFlowSlug string
|
flowSlug string
|
||||||
invalidationFlowSlug string
|
s *RadiusServer
|
||||||
s *RadiusServer
|
log *log.Entry
|
||||||
log *log.Entry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RadiusServer struct {
|
type RadiusServer struct {
|
||||||
|
|||||||
@ -14,27 +14,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (ws *WebServer) configureStatic() {
|
func (ws *WebServer) configureStatic() {
|
||||||
staticRouter := ws.lh.NewRoute().Subrouter()
|
statRouter := ws.lh.NewRoute().Subrouter()
|
||||||
staticRouter.Use(ws.staticHeaderMiddleware)
|
statRouter.Use(ws.staticHeaderMiddleware)
|
||||||
staticRouter.Use(web.DisableIndex)
|
indexLessRouter := statRouter.NewRoute().Subrouter()
|
||||||
|
indexLessRouter.Use(web.DisableIndex)
|
||||||
distFs := http.FileServer(http.Dir("./web/dist"))
|
distFs := http.FileServer(http.Dir("./web/dist"))
|
||||||
|
distHandler := http.StripPrefix("/static/dist/", distFs)
|
||||||
authentikHandler := http.StripPrefix("/static/authentik/", http.FileServer(http.Dir("./web/authentik")))
|
authentikHandler := http.StripPrefix("/static/authentik/", http.FileServer(http.Dir("./web/authentik")))
|
||||||
|
helpHandler := http.FileServer(http.Dir("./website/help/"))
|
||||||
|
indexLessRouter.PathPrefix("/static/dist/").Handler(distHandler)
|
||||||
|
indexLessRouter.PathPrefix("/static/authentik/").Handler(authentikHandler)
|
||||||
|
|
||||||
// Root file paths, from which they should be accessed
|
// Prevent font-loading issues on safari, which loads fonts relatively to the URL the browser is on
|
||||||
staticRouter.PathPrefix("/static/dist/").Handler(http.StripPrefix("/static/dist/", distFs))
|
indexLessRouter.PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
staticRouter.PathPrefix("/static/authentik/").Handler(authentikHandler)
|
|
||||||
|
|
||||||
// Also serve assets folder in specific interfaces since fonts in patternfly are imported
|
|
||||||
// with a relative path
|
|
||||||
staticRouter.PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
web.DisableIndex(http.StripPrefix(fmt.Sprintf("/if/flow/%s", vars["flow_slug"]), distFs)).ServeHTTP(rw, r)
|
web.DisableIndex(http.StripPrefix(fmt.Sprintf("/if/flow/%s", vars["flow_slug"]), distFs)).ServeHTTP(rw, r)
|
||||||
})
|
})
|
||||||
staticRouter.PathPrefix("/if/admin/assets").Handler(http.StripPrefix("/if/admin", distFs))
|
indexLessRouter.PathPrefix("/if/admin/assets").Handler(http.StripPrefix("/if/admin", distFs))
|
||||||
staticRouter.PathPrefix("/if/user/assets").Handler(http.StripPrefix("/if/user", distFs))
|
indexLessRouter.PathPrefix("/if/user/assets").Handler(http.StripPrefix("/if/user", distFs))
|
||||||
staticRouter.PathPrefix("/if/rac/{app_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
indexLessRouter.PathPrefix("/if/rac/{app_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
web.DisableIndex(http.StripPrefix(fmt.Sprintf("/if/rac/%s", vars["app_slug"]), distFs)).ServeHTTP(rw, r)
|
web.DisableIndex(http.StripPrefix(fmt.Sprintf("/if/rac/%s", vars["app_slug"]), distFs)).ServeHTTP(rw, r)
|
||||||
@ -43,13 +42,12 @@ func (ws *WebServer) configureStatic() {
|
|||||||
// Media files, if backend is file
|
// Media files, if backend is file
|
||||||
if config.Get().Storage.Media.Backend == "file" {
|
if config.Get().Storage.Media.Backend == "file" {
|
||||||
fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path))
|
fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path))
|
||||||
staticRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fsMedia))
|
indexLessRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fsMedia))
|
||||||
}
|
}
|
||||||
|
|
||||||
staticRouter.PathPrefix("/if/help/").Handler(http.StripPrefix("/if/help/", http.FileServer(http.Dir("./website/help/"))))
|
statRouter.PathPrefix("/if/help/").Handler(http.StripPrefix("/if/help/", helpHandler))
|
||||||
staticRouter.PathPrefix("/help").Handler(http.RedirectHandler("/if/help/", http.StatusMovedPermanently))
|
statRouter.PathPrefix("/help").Handler(http.RedirectHandler("/if/help/", http.StatusMovedPermanently))
|
||||||
|
|
||||||
// Static misc files
|
|
||||||
ws.lh.Path("/robots.txt").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
ws.lh.Path("/robots.txt").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
rw.Header()["Content-Type"] = []string{"text/plain"}
|
rw.Header()["Content-Type"] = []string{"text/plain"}
|
||||||
rw.WriteHeader(200)
|
rw.WriteHeader(200)
|
||||||
|
|||||||
@ -64,7 +64,6 @@ def release_lock(cursor: Cursor):
|
|||||||
"""Release database lock"""
|
"""Release database lock"""
|
||||||
if not LOCKED:
|
if not LOCKED:
|
||||||
return
|
return
|
||||||
LOGGER.info("releasing database lock")
|
|
||||||
cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,))
|
cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
from lifecycle.migrate import BaseMigration
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(BaseMigration):
|
|
||||||
def needs_migration(self) -> bool:
|
|
||||||
self.cur.execute(
|
|
||||||
"SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'template';"
|
|
||||||
)
|
|
||||||
return not bool(self.cur.rowcount)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.cur.execute("CREATE SCHEMA IF NOT EXISTS template; COMMIT;")
|
|
||||||
@ -116,7 +116,6 @@ class Migration(BaseMigration):
|
|||||||
host=CONFIG.get("redis.host"),
|
host=CONFIG.get("redis.host"),
|
||||||
port=6379,
|
port=6379,
|
||||||
db=db,
|
db=db,
|
||||||
username=CONFIG.get("redis.username"),
|
|
||||||
password=CONFIG.get("redis.password"),
|
password=CONFIG.get("redis.password"),
|
||||||
)
|
)
|
||||||
redis.flushall()
|
redis.flushall()
|
||||||
|
|||||||
@ -19,7 +19,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-03-05 00:07+0000\n"
|
"POT-Creation-Date: 2024-03-01 00:07+0000\n"
|
||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||||
"Last-Translator: Marc Schmitt, 2024\n"
|
"Last-Translator: Marc Schmitt, 2024\n"
|
||||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||||
@ -802,10 +802,6 @@ msgstr "Jeton du flux"
|
|||||||
msgid "Flow Tokens"
|
msgid "Flow Tokens"
|
||||||
msgstr "Jetons du flux"
|
msgstr "Jetons du flux"
|
||||||
|
|
||||||
#: authentik/flows/views/executor.py
|
|
||||||
msgid "Invalid next URL"
|
|
||||||
msgstr "URL suivante invalide"
|
|
||||||
|
|
||||||
#: authentik/lib/utils/time.py
|
#: authentik/lib/utils/time.py
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'."
|
msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'."
|
||||||
|
|||||||
305
poetry.lock
generated
305
poetry.lock
generated
@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
@ -392,33 +392,33 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "24.3.0"
|
version = "24.2.0"
|
||||||
description = "The uncompromising code formatter."
|
description = "The uncompromising code formatter."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
|
{file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"},
|
||||||
{file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
|
{file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"},
|
||||||
{file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
|
{file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"},
|
||||||
{file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
|
{file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"},
|
||||||
{file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
|
{file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"},
|
||||||
{file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
|
{file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"},
|
||||||
{file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
|
{file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"},
|
||||||
{file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
|
{file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"},
|
||||||
{file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
|
{file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"},
|
||||||
{file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
|
{file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"},
|
||||||
{file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
|
{file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"},
|
||||||
{file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
|
{file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"},
|
||||||
{file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
|
{file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"},
|
||||||
{file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
|
{file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"},
|
||||||
{file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
|
{file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"},
|
||||||
{file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
|
{file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"},
|
||||||
{file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
|
{file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"},
|
||||||
{file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
|
{file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"},
|
||||||
{file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
|
{file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"},
|
||||||
{file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
|
{file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"},
|
||||||
{file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
|
{file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"},
|
||||||
{file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
|
{file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -455,19 +455,19 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "botocore"
|
name = "botocore"
|
||||||
version = "1.34.63"
|
version = "1.34.15"
|
||||||
description = "Low-level, data-driven core of boto 3."
|
description = "Low-level, data-driven core of boto 3."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">= 3.8"
|
python-versions = ">= 3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "botocore-1.34.63-py3-none-any.whl", hash = "sha256:8a6cbc3a5c5988725c00815f8f7f6baf81980b19d9a2ee414b031e726759dba9"},
|
{file = "botocore-1.34.15-py3-none-any.whl", hash = "sha256:16bcf871e67ef0177593f06e9e5bae4db51c9a9a2e953cb14feeb42d53441a85"},
|
||||||
{file = "botocore-1.34.63.tar.gz", hash = "sha256:2237743fc3ed68319bc358b451e7c13a02110242b1522b839806fd64fcee45fb"},
|
{file = "botocore-1.34.15.tar.gz", hash = "sha256:c3c3404962a6d9d5e1634bd70ed53b8eff1ff17ee9d7a6240e9e8c94db48ad6f"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
jmespath = ">=0.7.1,<2.0.0"
|
jmespath = ">=0.7.1,<2.0.0"
|
||||||
python-dateutil = ">=2.1,<3.0.0"
|
python-dateutil = ">=2.1,<3.0.0"
|
||||||
urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""}
|
urllib3 = {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
crt = ["awscrt (==0.19.19)"]
|
crt = ["awscrt (==0.19.19)"]
|
||||||
@ -919,63 +919,63 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "coverage"
|
name = "coverage"
|
||||||
version = "7.4.4"
|
version = "7.4.3"
|
||||||
description = "Code coverage measurement for Python"
|
description = "Code coverage measurement for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
|
{file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
|
{file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
|
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
|
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
|
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
|
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
|
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
|
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
|
{file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
|
||||||
{file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
|
{file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
|
{file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
|
{file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
|
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
|
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
|
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
|
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
|
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
|
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
|
{file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
|
||||||
{file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
|
{file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
|
{file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
|
{file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
|
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
|
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
|
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
|
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
|
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
|
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
|
{file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
|
||||||
{file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
|
{file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
|
{file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
|
{file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
|
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
|
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
|
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
|
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
|
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
|
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
|
{file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
|
||||||
{file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
|
{file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
|
{file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
|
{file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
|
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
|
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
|
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
|
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
|
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
|
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
|
{file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
|
||||||
{file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
|
{file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
|
||||||
{file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
|
{file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
|
||||||
{file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
|
{file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
@ -1142,13 +1142,13 @@ bcrypt = ["bcrypt"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-filter"
|
name = "django-filter"
|
||||||
version = "24.2"
|
version = "24.1"
|
||||||
description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
|
description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "django-filter-24.2.tar.gz", hash = "sha256:48e5fc1da3ccd6ca0d5f9bb550973518ce977a4edde9d2a8a154a7f4f0b9f96e"},
|
{file = "django-filter-24.1.tar.gz", hash = "sha256:65cb43ce272077e5ac6aae1054d76c121cd6b552e296a82a13921e9371baf8c1"},
|
||||||
{file = "django_filter-24.2-py3-none-any.whl", hash = "sha256:df2ee9857e18d38bed203c8745f62a803fa0f31688c9fe6f8e868120b1848e48"},
|
{file = "django_filter-24.1-py3-none-any.whl", hash = "sha256:335bcae6cbd3e984b024841070f567b22faea57594f27d37c52f8f131f8d8621"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -1389,13 +1389,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "duo-client"
|
name = "duo-client"
|
||||||
version = "5.3.0"
|
version = "5.2.0"
|
||||||
description = "Reference client for Duo Security APIs"
|
description = "Reference client for Duo Security APIs"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "duo_client-5.3.0-py3-none-any.whl", hash = "sha256:85614bb684cef96285268aef0c1e858df939f6e8a190fb2c707d700bb0215766"},
|
{file = "duo_client-5.2.0-py3-none-any.whl", hash = "sha256:da3237e34300665c40ba5215f1e6656fec1a0136295917541aa973e7fcbf027e"},
|
||||||
{file = "duo_client-5.3.0.tar.gz", hash = "sha256:afa5ef98a42f06965a2702ca41dba9c85c483abd945e0a440f0ec4871b7593bf"},
|
{file = "duo_client-5.2.0.tar.gz", hash = "sha256:f82361740792b06303f9721e7ba593916080461769396b4f73c0502c0bfcee44"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -1712,13 +1712,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "importlib-metadata"
|
name = "importlib-metadata"
|
||||||
version = "7.1.0"
|
version = "7.0.2"
|
||||||
description = "Read metadata from Python packages"
|
description = "Read metadata from Python packages"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
|
{file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"},
|
||||||
{file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
|
{file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -1727,7 +1727,7 @@ zipp = ">=0.5"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||||
perf = ["ipython"]
|
perf = ["ipython"]
|
||||||
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
|
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "incremental"
|
name = "incremental"
|
||||||
@ -3332,30 +3332,32 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests-mock"
|
name = "requests-mock"
|
||||||
version = "1.12.1"
|
version = "1.11.0"
|
||||||
description = "Mock out responses from the requests package"
|
description = "Mock out responses from the requests package"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"},
|
{file = "requests-mock-1.11.0.tar.gz", hash = "sha256:ef10b572b489a5f28e09b708697208c4a3b2b89ef80a9f01584340ea357ec3c4"},
|
||||||
{file = "requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563"},
|
{file = "requests_mock-1.11.0-py2.py3-none-any.whl", hash = "sha256:f7fae383f228633f6bececebdab236c478ace2284d6292c6e7e2867b9ab74d15"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
requests = ">=2.22,<3"
|
requests = ">=2.3,<3"
|
||||||
|
six = "*"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
fixture = ["fixtures"]
|
fixture = ["fixtures"]
|
||||||
|
test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests-oauthlib"
|
name = "requests-oauthlib"
|
||||||
version = "2.0.0"
|
version = "1.4.0"
|
||||||
description = "OAuthlib authentication support for Requests."
|
description = "OAuthlib authentication support for Requests."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.4"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
files = [
|
files = [
|
||||||
{file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"},
|
{file = "requests-oauthlib-1.4.0.tar.gz", hash = "sha256:acee623221e4a39abcbb919312c8ff04bd44e7e417087fb4bd5e2a2f53d5e79a"},
|
||||||
{file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"},
|
{file = "requests_oauthlib-1.4.0-py2.py3-none-any.whl", hash = "sha256:7a3130d94a17520169e38db6c8d75f2c974643788465ecc2e4b36d288bf13033"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -3507,28 +3509,28 @@ pyasn1 = ">=0.1.3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.3.4"
|
version = "0.3.2"
|
||||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"},
|
{file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77f2612752e25f730da7421ca5e3147b213dca4f9a0f7e0b534e9562c5441f01"},
|
||||||
{file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"},
|
{file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9966b964b2dd1107797be9ca7195002b874424d1d5472097701ae8f43eadef5d"},
|
||||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"},
|
{file = "ruff-0.3.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b83d17ff166aa0659d1e1deaf9f2f14cbe387293a906de09bc4860717eb2e2da"},
|
||||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"},
|
{file = "ruff-0.3.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb875c6cc87b3703aeda85f01c9aebdce3d217aeaca3c2e52e38077383f7268a"},
|
||||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"},
|
{file = "ruff-0.3.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be75e468a6a86426430373d81c041b7605137a28f7014a72d2fc749e47f572aa"},
|
||||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"},
|
{file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:967978ac2d4506255e2f52afe70dda023fc602b283e97685c8447d036863a302"},
|
||||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"},
|
{file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1231eacd4510f73222940727ac927bc5d07667a86b0cbe822024dd00343e77e9"},
|
||||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"},
|
{file = "ruff-0.3.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6d613b19e9a8021be2ee1d0e27710208d1603b56f47203d0abbde906929a9b"},
|
||||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"},
|
{file = "ruff-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8439338a6303585d27b66b4626cbde89bb3e50fa3cae86ce52c1db7449330a7"},
|
||||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"},
|
{file = "ruff-0.3.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:de8b480d8379620cbb5ea466a9e53bb467d2fb07c7eca54a4aa8576483c35d36"},
|
||||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"},
|
{file = "ruff-0.3.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b74c3de9103bd35df2bb05d8b2899bf2dbe4efda6474ea9681280648ec4d237d"},
|
||||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"},
|
{file = "ruff-0.3.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f380be9fc15a99765c9cf316b40b9da1f6ad2ab9639e551703e581a5e6da6745"},
|
||||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"},
|
{file = "ruff-0.3.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0ac06a3759c3ab9ef86bbeca665d31ad3aa9a4b1c17684aadb7e61c10baa0df4"},
|
||||||
{file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"},
|
{file = "ruff-0.3.2-py3-none-win32.whl", hash = "sha256:9bd640a8f7dd07a0b6901fcebccedadeb1a705a50350fb86b4003b805c81385a"},
|
||||||
{file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"},
|
{file = "ruff-0.3.2-py3-none-win_amd64.whl", hash = "sha256:0c1bdd9920cab5707c26c8b3bf33a064a4ca7842d91a99ec0634fec68f9f4037"},
|
||||||
{file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"},
|
{file = "ruff-0.3.2-py3-none-win_arm64.whl", hash = "sha256:5f65103b1d76e0d600cabd577b04179ff592064eaa451a70a81085930e907d0b"},
|
||||||
{file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"},
|
{file = "ruff-0.3.2.tar.gz", hash = "sha256:fa78ec9418eb1ca3db392811df3376b46471ae93792a81af2d1cbb0e5dcb5142"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3550,13 +3552,13 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selenium"
|
name = "selenium"
|
||||||
version = "4.19.0"
|
version = "4.18.1"
|
||||||
description = ""
|
description = ""
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "selenium-4.19.0-py3-none-any.whl", hash = "sha256:5b4f49240d61e687a73f7968ae2517d403882aae3550eae2a229c745e619f1d9"},
|
{file = "selenium-4.18.1-py3-none-any.whl", hash = "sha256:b24a3cdd2d47c29832e81345bfcde0c12bb608738013e53c781b211b418df241"},
|
||||||
{file = "selenium-4.19.0.tar.gz", hash = "sha256:d9dfd6d0b021d71d0a48b865fe7746490ba82b81e9c87b212360006629eb1853"},
|
{file = "selenium-4.18.1.tar.gz", hash = "sha256:a11f67afa8bfac6b77e148c987b33f6b14eb1cae4d352722a75de1f26e3f0ae2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -3568,13 +3570,13 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-sdk"
|
name = "sentry-sdk"
|
||||||
version = "1.44.0"
|
version = "1.41.0"
|
||||||
description = "Python client for Sentry (https://sentry.io)"
|
description = "Python client for Sentry (https://sentry.io)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "sentry-sdk-1.44.0.tar.gz", hash = "sha256:f7125a9235795811962d52ff796dc032cd1d0dd98b59beaced8380371cd9c13c"},
|
{file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"},
|
||||||
{file = "sentry_sdk-1.44.0-py2.py3-none-any.whl", hash = "sha256:eb65289da013ca92fad2694851ad2f086aa3825e808dc285bd7dcaf63602bb18"},
|
{file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -3588,7 +3590,6 @@ asyncpg = ["asyncpg (>=0.23)"]
|
|||||||
beam = ["apache-beam (>=2.12)"]
|
beam = ["apache-beam (>=2.12)"]
|
||||||
bottle = ["bottle (>=0.12.13)"]
|
bottle = ["bottle (>=0.12.13)"]
|
||||||
celery = ["celery (>=3)"]
|
celery = ["celery (>=3)"]
|
||||||
celery-redbeat = ["celery-redbeat (>=2)"]
|
|
||||||
chalice = ["chalice (>=1.16.0)"]
|
chalice = ["chalice (>=1.16.0)"]
|
||||||
clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
|
clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
|
||||||
django = ["django (>=1.8)"]
|
django = ["django (>=1.8)"]
|
||||||
@ -3599,7 +3600,6 @@ grpcio = ["grpcio (>=1.21.1)"]
|
|||||||
httpx = ["httpx (>=0.16.0)"]
|
httpx = ["httpx (>=0.16.0)"]
|
||||||
huey = ["huey (>=2)"]
|
huey = ["huey (>=2)"]
|
||||||
loguru = ["loguru (>=0.5)"]
|
loguru = ["loguru (>=0.5)"]
|
||||||
openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
|
|
||||||
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
||||||
opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
|
opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
|
||||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||||
@ -3818,13 +3818,13 @@ wsproto = ">=0.14"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "twilio"
|
name = "twilio"
|
||||||
version = "9.0.2"
|
version = "9.0.1"
|
||||||
description = "Twilio API client and TwiML generator"
|
description = "Twilio API client and TwiML generator"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7.0"
|
python-versions = ">=3.7.0"
|
||||||
files = [
|
files = [
|
||||||
{file = "twilio-9.0.2-py2.py3-none-any.whl", hash = "sha256:db89a8326f92240cdd8dc1dafb1d3f69169576243be9b9cbf5cf7778ecab0eed"},
|
{file = "twilio-9.0.1-py2.py3-none-any.whl", hash = "sha256:7df45f314140b5931199420a2a00f0466d2fd16aff2f8c3e1589a47adc9deecb"},
|
||||||
{file = "twilio-9.0.2.tar.gz", hash = "sha256:9450b7a9987c32146d1760c8680f92baa76b4ba570543758a4937da48ae46d77"},
|
{file = "twilio-9.0.1.tar.gz", hash = "sha256:4ffb63342dff9a5b24dd3f8d33e8fac2ceb55dcd10936d50456cbb44e1834e72"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -3962,33 +3962,48 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.2.1"
|
version = "1.26.18"
|
||||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||||
files = [
|
files = [
|
||||||
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
|
{file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"},
|
||||||
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
|
{file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pysocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""}
|
certifi = {version = "*", optional = true, markers = "extra == \"secure\""}
|
||||||
|
cryptography = {version = ">=1.3.4", optional = true, markers = "extra == \"secure\""}
|
||||||
|
idna = {version = ">=2.0.0", optional = true, markers = "extra == \"secure\""}
|
||||||
|
pyOpenSSL = {version = ">=0.14", optional = true, markers = "extra == \"secure\""}
|
||||||
|
PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""}
|
||||||
|
urllib3-secure-extra = {version = "*", optional = true, markers = "extra == \"secure\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
|
brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
|
||||||
h2 = ["h2 (>=4,<5)"]
|
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
|
||||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
zstd = ["zstandard (>=0.18.0)"]
|
|
||||||
|
[[package]]
|
||||||
|
name = "urllib3-secure-extra"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Marker library to detect whether urllib3 was installed with the deprecated [secure] extra"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "urllib3-secure-extra-0.1.0.tar.gz", hash = "sha256:ee9409cbfeb4b8609047be4c32fb4317870c602767e53fd8a41005ebe6a41dff"},
|
||||||
|
{file = "urllib3_secure_extra-0.1.0-py2.py3-none-any.whl", hash = "sha256:f7adcb108b4d12a4b26b99eb60e265d087f435052a76aefa396b6ee85e9a6ef9"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvicorn"
|
name = "uvicorn"
|
||||||
version = "0.29.0"
|
version = "0.28.0"
|
||||||
description = "The lightning-fast ASGI server."
|
description = "The lightning-fast ASGI server."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
|
{file = "uvicorn-0.28.0-py3-none-any.whl", hash = "sha256:6623abbbe6176204a4226e67607b4d52cc60ff62cda0ff177613645cefa2ece1"},
|
||||||
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
|
{file = "uvicorn-0.28.0.tar.gz", hash = "sha256:cab4473b5d1eaeb5a0f6375ac4bc85007ffc75c3cc1768816d9e5d589857b067"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -4201,13 +4216,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webauthn"
|
name = "webauthn"
|
||||||
version = "2.1.0"
|
version = "2.0.0"
|
||||||
description = "Pythonic WebAuthn"
|
description = "Pythonic WebAuthn"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
{file = "webauthn-2.1.0-py3-none-any.whl", hash = "sha256:9e1cf916e5ed7c01d54a6dfcc19dacbd2b87b81d2648f001b1fcbcb7aa2ff130"},
|
{file = "webauthn-2.0.0-py3-none-any.whl", hash = "sha256:644dc68af5caaade06be6a2a2278775e85116e92dd755ad7a49d992d51c82033"},
|
||||||
{file = "webauthn-2.1.0.tar.gz", hash = "sha256:b196a4246c2818820857ba195c6e6e5398c761117f2269e3d2deab11c7995fc4"},
|
{file = "webauthn-2.0.0.tar.gz", hash = "sha256:12cc1759da98668b8242badc37c4129df300f89d89f5c183fac80e7b33c41dfd"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -4536,4 +4551,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "~3.12"
|
python-versions = "~3.12"
|
||||||
content-hash = "04ef13e2692c158e5eda1e89876a215746c56a891a161d8434d808dc12c0fc7a"
|
content-hash = "825f1d552ba34206f7bfd55b70bfb42bc5d769605f59410703828ae787cd0baf"
|
||||||
|
|||||||
@ -140,7 +140,7 @@ twilio = "*"
|
|||||||
twisted = "*"
|
twisted = "*"
|
||||||
ua-parser = "*"
|
ua-parser = "*"
|
||||||
# Pinned because of botocore https://github.com/orgs/python-poetry/discussions/7937
|
# Pinned because of botocore https://github.com/orgs/python-poetry/discussions/7937
|
||||||
urllib3 = { extras = ["secure"], version = "<3" }
|
urllib3 = { extras = ["secure"], version = "<2" }
|
||||||
uvicorn = { extras = ["standard"], version = "*" }
|
uvicorn = { extras = ["standard"], version = "*" }
|
||||||
watchdog = "*"
|
watchdog = "*"
|
||||||
webauthn = "*"
|
webauthn = "*"
|
||||||
|
|||||||
493
schema.yml
493
schema.yml
@ -18512,7 +18512,6 @@ paths:
|
|||||||
- authentik_stages_password.passwordstage
|
- authentik_stages_password.passwordstage
|
||||||
- authentik_stages_prompt.prompt
|
- authentik_stages_prompt.prompt
|
||||||
- authentik_stages_prompt.promptstage
|
- authentik_stages_prompt.promptstage
|
||||||
- authentik_stages_source.sourcestage
|
|
||||||
- authentik_stages_user_delete.userdeletestage
|
- authentik_stages_user_delete.userdeletestage
|
||||||
- authentik_stages_user_login.userloginstage
|
- authentik_stages_user_login.userloginstage
|
||||||
- authentik_stages_user_logout.userlogoutstage
|
- authentik_stages_user_logout.userlogoutstage
|
||||||
@ -18588,7 +18587,6 @@ paths:
|
|||||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||||
* `authentik_stages_source.sourcestage` - Source Stage
|
|
||||||
* `authentik_events.event` - Event
|
* `authentik_events.event` - Event
|
||||||
* `authentik_events.notificationtransport` - Notification Transport
|
* `authentik_events.notificationtransport` - Notification Transport
|
||||||
* `authentik_events.notification` - Notification
|
* `authentik_events.notification` - Notification
|
||||||
@ -18802,7 +18800,6 @@ paths:
|
|||||||
- authentik_stages_password.passwordstage
|
- authentik_stages_password.passwordstage
|
||||||
- authentik_stages_prompt.prompt
|
- authentik_stages_prompt.prompt
|
||||||
- authentik_stages_prompt.promptstage
|
- authentik_stages_prompt.promptstage
|
||||||
- authentik_stages_source.sourcestage
|
|
||||||
- authentik_stages_user_delete.userdeletestage
|
- authentik_stages_user_delete.userdeletestage
|
||||||
- authentik_stages_user_login.userloginstage
|
- authentik_stages_user_login.userloginstage
|
||||||
- authentik_stages_user_logout.userlogoutstage
|
- authentik_stages_user_logout.userlogoutstage
|
||||||
@ -18878,7 +18875,6 @@ paths:
|
|||||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||||
* `authentik_stages_source.sourcestage` - Source Stage
|
|
||||||
* `authentik_events.event` - Event
|
* `authentik_events.event` - Event
|
||||||
* `authentik_events.notificationtransport` - Notification Transport
|
* `authentik_events.notificationtransport` - Notification Transport
|
||||||
* `authentik_events.notification` - Notification
|
* `authentik_events.notification` - Notification
|
||||||
@ -19926,10 +19922,6 @@ paths:
|
|||||||
description: Number of results to return per page.
|
description: Number of results to return per page.
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
- in: query
|
|
||||||
name: password_login_update_internal_password
|
|
||||||
schema:
|
|
||||||
type: boolean
|
|
||||||
- in: query
|
- in: query
|
||||||
name: peer_certificate
|
name: peer_certificate
|
||||||
schema:
|
schema:
|
||||||
@ -27880,289 +27872,6 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
description: ''
|
description: ''
|
||||||
/stages/source/:
|
|
||||||
get:
|
|
||||||
operationId: stages_source_list
|
|
||||||
description: SourceStage Viewset
|
|
||||||
parameters:
|
|
||||||
- in: query
|
|
||||||
name: name
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- name: ordering
|
|
||||||
required: false
|
|
||||||
in: query
|
|
||||||
description: Which field to use when ordering the results.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- name: page
|
|
||||||
required: false
|
|
||||||
in: query
|
|
||||||
description: A page number within the paginated result set.
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
- name: page_size
|
|
||||||
required: false
|
|
||||||
in: query
|
|
||||||
description: Number of results to return per page.
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
- in: query
|
|
||||||
name: resume_timeout
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- name: search
|
|
||||||
required: false
|
|
||||||
in: query
|
|
||||||
description: A search term.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: source
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
- in: query
|
|
||||||
name: stage_uuid
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
tags:
|
|
||||||
- stages
|
|
||||||
security:
|
|
||||||
- authentik: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/PaginatedSourceStageList'
|
|
||||||
description: ''
|
|
||||||
'400':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ValidationError'
|
|
||||||
description: ''
|
|
||||||
'403':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/GenericError'
|
|
||||||
description: ''
|
|
||||||
post:
|
|
||||||
operationId: stages_source_create
|
|
||||||
description: SourceStage Viewset
|
|
||||||
tags:
|
|
||||||
- stages
|
|
||||||
requestBody:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/SourceStageRequest'
|
|
||||||
required: true
|
|
||||||
security:
|
|
||||||
- authentik: []
|
|
||||||
responses:
|
|
||||||
'201':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/SourceStage'
|
|
||||||
description: ''
|
|
||||||
'400':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ValidationError'
|
|
||||||
description: ''
|
|
||||||
'403':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/GenericError'
|
|
||||||
description: ''
|
|
||||||
/stages/source/{stage_uuid}/:
|
|
||||||
get:
|
|
||||||
operationId: stages_source_retrieve
|
|
||||||
description: SourceStage Viewset
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: stage_uuid
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
description: A UUID string identifying this Source Stage.
|
|
||||||
required: true
|
|
||||||
tags:
|
|
||||||
- stages
|
|
||||||
security:
|
|
||||||
- authentik: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/SourceStage'
|
|
||||||
description: ''
|
|
||||||
'400':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ValidationError'
|
|
||||||
description: ''
|
|
||||||
'403':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/GenericError'
|
|
||||||
description: ''
|
|
||||||
put:
|
|
||||||
operationId: stages_source_update
|
|
||||||
description: SourceStage Viewset
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: stage_uuid
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
description: A UUID string identifying this Source Stage.
|
|
||||||
required: true
|
|
||||||
tags:
|
|
||||||
- stages
|
|
||||||
requestBody:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/SourceStageRequest'
|
|
||||||
required: true
|
|
||||||
security:
|
|
||||||
- authentik: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/SourceStage'
|
|
||||||
description: ''
|
|
||||||
'400':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ValidationError'
|
|
||||||
description: ''
|
|
||||||
'403':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/GenericError'
|
|
||||||
description: ''
|
|
||||||
patch:
|
|
||||||
operationId: stages_source_partial_update
|
|
||||||
description: SourceStage Viewset
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: stage_uuid
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
description: A UUID string identifying this Source Stage.
|
|
||||||
required: true
|
|
||||||
tags:
|
|
||||||
- stages
|
|
||||||
requestBody:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/PatchedSourceStageRequest'
|
|
||||||
security:
|
|
||||||
- authentik: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/SourceStage'
|
|
||||||
description: ''
|
|
||||||
'400':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ValidationError'
|
|
||||||
description: ''
|
|
||||||
'403':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/GenericError'
|
|
||||||
description: ''
|
|
||||||
delete:
|
|
||||||
operationId: stages_source_destroy
|
|
||||||
description: SourceStage Viewset
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: stage_uuid
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
description: A UUID string identifying this Source Stage.
|
|
||||||
required: true
|
|
||||||
tags:
|
|
||||||
- stages
|
|
||||||
security:
|
|
||||||
- authentik: []
|
|
||||||
responses:
|
|
||||||
'204':
|
|
||||||
description: No response body
|
|
||||||
'400':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ValidationError'
|
|
||||||
description: ''
|
|
||||||
'403':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/GenericError'
|
|
||||||
description: ''
|
|
||||||
/stages/source/{stage_uuid}/used_by/:
|
|
||||||
get:
|
|
||||||
operationId: stages_source_used_by_list
|
|
||||||
description: Get a list of all objects that use this object
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: stage_uuid
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
description: A UUID string identifying this Source Stage.
|
|
||||||
required: true
|
|
||||||
tags:
|
|
||||||
- stages
|
|
||||||
security:
|
|
||||||
- authentik: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/UsedBy'
|
|
||||||
description: ''
|
|
||||||
'400':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ValidationError'
|
|
||||||
description: ''
|
|
||||||
'403':
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/GenericError'
|
|
||||||
description: ''
|
|
||||||
/stages/user_delete/:
|
/stages/user_delete/:
|
||||||
get:
|
get:
|
||||||
operationId: stages_user_delete_list
|
operationId: stages_user_delete_list
|
||||||
@ -29933,7 +29642,6 @@ components:
|
|||||||
- authentik.enterprise
|
- authentik.enterprise
|
||||||
- authentik.enterprise.audit
|
- authentik.enterprise.audit
|
||||||
- authentik.enterprise.providers.rac
|
- authentik.enterprise.providers.rac
|
||||||
- authentik.enterprise.stages.source
|
|
||||||
- authentik.events
|
- authentik.events
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: |-
|
||||||
@ -29988,7 +29696,6 @@ components:
|
|||||||
* `authentik.enterprise` - authentik Enterprise
|
* `authentik.enterprise` - authentik Enterprise
|
||||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
AppleChallengeResponseRequest:
|
AppleChallengeResponseRequest:
|
||||||
type: object
|
type: object
|
||||||
@ -31786,7 +31493,8 @@ components:
|
|||||||
pk:
|
pk:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
title: Connection token uuid
|
readOnly: true
|
||||||
|
title: Pbm uuid
|
||||||
provider:
|
provider:
|
||||||
type: integer
|
type: integer
|
||||||
provider_obj:
|
provider_obj:
|
||||||
@ -31796,6 +31504,7 @@ components:
|
|||||||
endpoint:
|
endpoint:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
readOnly: true
|
||||||
endpoint_obj:
|
endpoint_obj:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/Endpoint'
|
- $ref: '#/components/schemas/Endpoint'
|
||||||
@ -31807,6 +31516,7 @@ components:
|
|||||||
required:
|
required:
|
||||||
- endpoint
|
- endpoint
|
||||||
- endpoint_obj
|
- endpoint_obj
|
||||||
|
- pk
|
||||||
- provider
|
- provider
|
||||||
- provider_obj
|
- provider_obj
|
||||||
- user
|
- user
|
||||||
@ -31814,17 +31524,9 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
description: ConnectionToken Serializer
|
description: ConnectionToken Serializer
|
||||||
properties:
|
properties:
|
||||||
pk:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
title: Connection token uuid
|
|
||||||
provider:
|
provider:
|
||||||
type: integer
|
type: integer
|
||||||
endpoint:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
required:
|
required:
|
||||||
- endpoint
|
|
||||||
- provider
|
- provider
|
||||||
ConsentChallenge:
|
ConsentChallenge:
|
||||||
type: object
|
type: object
|
||||||
@ -32355,10 +32057,7 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
$ref: '#/components/schemas/ErrorDetail'
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
required:
|
required:
|
||||||
- name
|
|
||||||
- type
|
- type
|
||||||
DummyChallengeResponseRequest:
|
DummyChallengeResponseRequest:
|
||||||
type: object
|
type: object
|
||||||
@ -33040,7 +32739,6 @@ components:
|
|||||||
* `authentik.enterprise` - authentik Enterprise
|
* `authentik.enterprise` - authentik Enterprise
|
||||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
model:
|
model:
|
||||||
allOf:
|
allOf:
|
||||||
@ -33118,7 +32816,6 @@ components:
|
|||||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||||
* `authentik_stages_source.sourcestage` - Source Stage
|
|
||||||
* `authentik_events.event` - Event
|
* `authentik_events.event` - Event
|
||||||
* `authentik_events.notificationtransport` - Notification Transport
|
* `authentik_events.notificationtransport` - Notification Transport
|
||||||
* `authentik_events.notification` - Notification
|
* `authentik_events.notification` - Notification
|
||||||
@ -33241,7 +32938,6 @@ components:
|
|||||||
* `authentik.enterprise` - authentik Enterprise
|
* `authentik.enterprise` - authentik Enterprise
|
||||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
model:
|
model:
|
||||||
allOf:
|
allOf:
|
||||||
@ -33319,7 +33015,6 @@ components:
|
|||||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||||
* `authentik_stages_source.sourcestage` - Source Stage
|
|
||||||
* `authentik_events.event` - Event
|
* `authentik_events.event` - Event
|
||||||
* `authentik_events.notificationtransport` - Notification Transport
|
* `authentik_events.notificationtransport` - Notification Transport
|
||||||
* `authentik_events.notification` - Notification
|
* `authentik_events.notification` - Notification
|
||||||
@ -33782,7 +33477,8 @@ components:
|
|||||||
logs:
|
logs:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/LogEvent'
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
readOnly: true
|
readOnly: true
|
||||||
success:
|
success:
|
||||||
type: boolean
|
type: boolean
|
||||||
@ -34340,8 +34036,6 @@ components:
|
|||||||
type: boolean
|
type: boolean
|
||||||
application_pre:
|
application_pre:
|
||||||
type: string
|
type: string
|
||||||
flow_designation:
|
|
||||||
$ref: '#/components/schemas/FlowDesignationEnum'
|
|
||||||
enroll_url:
|
enroll_url:
|
||||||
type: string
|
type: string
|
||||||
recovery_url:
|
recovery_url:
|
||||||
@ -34357,7 +34051,6 @@ components:
|
|||||||
show_source_labels:
|
show_source_labels:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- flow_designation
|
|
||||||
- password_fields
|
- password_fields
|
||||||
- primary_action
|
- primary_action
|
||||||
- show_source_labels
|
- show_source_labels
|
||||||
@ -35228,10 +34921,6 @@ components:
|
|||||||
object_uniqueness_field:
|
object_uniqueness_field:
|
||||||
type: string
|
type: string
|
||||||
description: Field which contains a unique Identifier.
|
description: Field which contains a unique Identifier.
|
||||||
password_login_update_internal_password:
|
|
||||||
type: boolean
|
|
||||||
description: Update internal authentik password when login succeeds with
|
|
||||||
LDAP
|
|
||||||
sync_users:
|
sync_users:
|
||||||
type: boolean
|
type: boolean
|
||||||
sync_users_password:
|
sync_users_password:
|
||||||
@ -35373,10 +35062,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
description: Field which contains a unique Identifier.
|
description: Field which contains a unique Identifier.
|
||||||
password_login_update_internal_password:
|
|
||||||
type: boolean
|
|
||||||
description: Update internal authentik password when login succeeds with
|
|
||||||
LDAP
|
|
||||||
sync_users:
|
sync_users:
|
||||||
type: boolean
|
type: boolean
|
||||||
sync_users_password:
|
sync_users_password:
|
||||||
@ -35514,48 +35199,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- link
|
- link
|
||||||
LogEvent:
|
|
||||||
type: object
|
|
||||||
description: Single log message with all context logged.
|
|
||||||
properties:
|
|
||||||
timestamp:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
log_level:
|
|
||||||
$ref: '#/components/schemas/LogLevelEnum'
|
|
||||||
logger:
|
|
||||||
type: string
|
|
||||||
event:
|
|
||||||
type: string
|
|
||||||
attributes:
|
|
||||||
type: object
|
|
||||||
additionalProperties: {}
|
|
||||||
required:
|
|
||||||
- attributes
|
|
||||||
- event
|
|
||||||
- log_level
|
|
||||||
- logger
|
|
||||||
- timestamp
|
|
||||||
LogLevelEnum:
|
|
||||||
enum:
|
|
||||||
- critical
|
|
||||||
- exception
|
|
||||||
- error
|
|
||||||
- warn
|
|
||||||
- warning
|
|
||||||
- info
|
|
||||||
- debug
|
|
||||||
- notset
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
* `critical` - critical
|
|
||||||
* `exception` - exception
|
|
||||||
* `error` - error
|
|
||||||
* `warn` - warn
|
|
||||||
* `warning` - warning
|
|
||||||
* `info` - info
|
|
||||||
* `debug` - debug
|
|
||||||
* `notset` - notset
|
|
||||||
LoginChallengeTypes:
|
LoginChallengeTypes:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/RedirectChallenge'
|
- $ref: '#/components/schemas/RedirectChallenge'
|
||||||
@ -35687,7 +35330,6 @@ components:
|
|||||||
- authentik_providers_rac.racprovider
|
- authentik_providers_rac.racprovider
|
||||||
- authentik_providers_rac.endpoint
|
- authentik_providers_rac.endpoint
|
||||||
- authentik_providers_rac.racpropertymapping
|
- authentik_providers_rac.racpropertymapping
|
||||||
- authentik_stages_source.sourcestage
|
|
||||||
- authentik_events.event
|
- authentik_events.event
|
||||||
- authentik_events.notificationtransport
|
- authentik_events.notificationtransport
|
||||||
- authentik_events.notification
|
- authentik_events.notification
|
||||||
@ -35764,7 +35406,6 @@ components:
|
|||||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||||
* `authentik_stages_source.sourcestage` - Source Stage
|
|
||||||
* `authentik_events.event` - Event
|
* `authentik_events.event` - Event
|
||||||
* `authentik_events.notificationtransport` - Notification Transport
|
* `authentik_events.notificationtransport` - Notification Transport
|
||||||
* `authentik_events.notification` - Notification
|
* `authentik_events.notification` - Notification
|
||||||
@ -37699,18 +37340,6 @@ components:
|
|||||||
required:
|
required:
|
||||||
- pagination
|
- pagination
|
||||||
- results
|
- results
|
||||||
PaginatedSourceStageList:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
pagination:
|
|
||||||
$ref: '#/components/schemas/Pagination'
|
|
||||||
results:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/SourceStage'
|
|
||||||
required:
|
|
||||||
- pagination
|
|
||||||
- results
|
|
||||||
PaginatedStageList:
|
PaginatedStageList:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -38647,15 +38276,8 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
description: ConnectionToken Serializer
|
description: ConnectionToken Serializer
|
||||||
properties:
|
properties:
|
||||||
pk:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
title: Connection token uuid
|
|
||||||
provider:
|
provider:
|
||||||
type: integer
|
type: integer
|
||||||
endpoint:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
PatchedConsentStageRequest:
|
PatchedConsentStageRequest:
|
||||||
type: object
|
type: object
|
||||||
description: ConsentStage Serializer
|
description: ConsentStage Serializer
|
||||||
@ -38961,7 +38583,6 @@ components:
|
|||||||
* `authentik.enterprise` - authentik Enterprise
|
* `authentik.enterprise` - authentik Enterprise
|
||||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
|
||||||
* `authentik.events` - authentik Events
|
* `authentik.events` - authentik Events
|
||||||
model:
|
model:
|
||||||
allOf:
|
allOf:
|
||||||
@ -39039,7 +38660,6 @@ components:
|
|||||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||||
* `authentik_stages_source.sourcestage` - Source Stage
|
|
||||||
* `authentik_events.event` - Event
|
* `authentik_events.event` - Event
|
||||||
* `authentik_events.notificationtransport` - Notification Transport
|
* `authentik_events.notificationtransport` - Notification Transport
|
||||||
* `authentik_events.notification` - Notification
|
* `authentik_events.notification` - Notification
|
||||||
@ -39493,10 +39113,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
description: Field which contains a unique Identifier.
|
description: Field which contains a unique Identifier.
|
||||||
password_login_update_internal_password:
|
|
||||||
type: boolean
|
|
||||||
description: Update internal authentik password when login succeeds with
|
|
||||||
LDAP
|
|
||||||
sync_users:
|
sync_users:
|
||||||
type: boolean
|
type: boolean
|
||||||
sync_users_password:
|
sync_users_password:
|
||||||
@ -40626,25 +40242,6 @@ components:
|
|||||||
impersonation:
|
impersonation:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Globally enable/disable impersonation.
|
description: Globally enable/disable impersonation.
|
||||||
PatchedSourceStageRequest:
|
|
||||||
type: object
|
|
||||||
description: SourceStage Serializer
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
flow_set:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/FlowSetRequest'
|
|
||||||
source:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
resume_timeout:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
description: 'Amount of time a user can take to return from the source to
|
|
||||||
continue the flow (Format: hours=-1;minutes=-2;seconds=-3)'
|
|
||||||
PatchedStaticDeviceRequest:
|
PatchedStaticDeviceRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Serializer for static authenticator devices
|
description: Serializer for static authenticator devices
|
||||||
@ -41350,7 +40947,8 @@ components:
|
|||||||
log_messages:
|
log_messages:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/LogEvent'
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
readOnly: true
|
readOnly: true
|
||||||
required:
|
required:
|
||||||
- log_messages
|
- log_messages
|
||||||
@ -43988,74 +43586,6 @@ components:
|
|||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
- slug
|
- slug
|
||||||
SourceStage:
|
|
||||||
type: object
|
|
||||||
description: SourceStage Serializer
|
|
||||||
properties:
|
|
||||||
pk:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
readOnly: true
|
|
||||||
title: Stage uuid
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
description: Get object type so that we know how to edit the object
|
|
||||||
readOnly: true
|
|
||||||
verbose_name:
|
|
||||||
type: string
|
|
||||||
description: Return object's verbose_name
|
|
||||||
readOnly: true
|
|
||||||
verbose_name_plural:
|
|
||||||
type: string
|
|
||||||
description: Return object's plural verbose_name
|
|
||||||
readOnly: true
|
|
||||||
meta_model_name:
|
|
||||||
type: string
|
|
||||||
description: Return internal model name
|
|
||||||
readOnly: true
|
|
||||||
flow_set:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/FlowSet'
|
|
||||||
source:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
resume_timeout:
|
|
||||||
type: string
|
|
||||||
description: 'Amount of time a user can take to return from the source to
|
|
||||||
continue the flow (Format: hours=-1;minutes=-2;seconds=-3)'
|
|
||||||
required:
|
|
||||||
- component
|
|
||||||
- meta_model_name
|
|
||||||
- name
|
|
||||||
- pk
|
|
||||||
- source
|
|
||||||
- verbose_name
|
|
||||||
- verbose_name_plural
|
|
||||||
SourceStageRequest:
|
|
||||||
type: object
|
|
||||||
description: SourceStage Serializer
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
flow_set:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/FlowSetRequest'
|
|
||||||
source:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
resume_timeout:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
description: 'Amount of time a user can take to return from the source to
|
|
||||||
continue the flow (Format: hours=-1;minutes=-2;seconds=-3)'
|
|
||||||
required:
|
|
||||||
- name
|
|
||||||
- source
|
|
||||||
SourceType:
|
SourceType:
|
||||||
type: object
|
type: object
|
||||||
description: Serializer for SourceType
|
description: Serializer for SourceType
|
||||||
@ -44364,7 +43894,7 @@ components:
|
|||||||
messages:
|
messages:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/LogEvent'
|
type: string
|
||||||
required:
|
required:
|
||||||
- description
|
- description
|
||||||
- duration
|
- duration
|
||||||
@ -45654,10 +45184,6 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
description: Get latest version from cache
|
description: Get latest version from cache
|
||||||
readOnly: true
|
readOnly: true
|
||||||
version_latest_valid:
|
|
||||||
type: boolean
|
|
||||||
description: Check if latest version is valid
|
|
||||||
readOnly: true
|
|
||||||
build_hash:
|
build_hash:
|
||||||
type: string
|
type: string
|
||||||
description: Get build hash, if version is not latest or released
|
description: Get build hash, if version is not latest or released
|
||||||
@ -45671,7 +45197,6 @@ components:
|
|||||||
- outdated
|
- outdated
|
||||||
- version_current
|
- version_current
|
||||||
- version_latest
|
- version_latest
|
||||||
- version_latest_valid
|
|
||||||
WebAuthnDevice:
|
WebAuthnDevice:
|
||||||
type: object
|
type: object
|
||||||
description: Serializer for WebAuthn authenticator devices
|
description: Serializer for WebAuthn authenticator devices
|
||||||
|
|||||||
@ -128,7 +128,6 @@ class TestSourceLDAPSamba(SeleniumTestCase):
|
|||||||
base_dn="dc=test,dc=goauthentik,dc=io",
|
base_dn="dc=test,dc=goauthentik,dc=io",
|
||||||
additional_user_dn="ou=users",
|
additional_user_dn="ou=users",
|
||||||
additional_group_dn="ou=groups",
|
additional_group_dn="ou=groups",
|
||||||
password_login_update_internal_password=True,
|
|
||||||
)
|
)
|
||||||
source.property_mappings.set(
|
source.property_mappings.set(
|
||||||
LDAPPropertyMapping.objects.filter(
|
LDAPPropertyMapping.objects.filter(
|
||||||
|
|||||||
@ -10,7 +10,6 @@ from kubernetes.client.exceptions import OpenApiException
|
|||||||
from authentik.core.tests.utils import create_test_flow
|
from authentik.core.tests.utils import create_test_flow
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
||||||
from authentik.outposts.controllers.k8s.service import ServiceReconciler
|
|
||||||
from authentik.outposts.controllers.k8s.triggers import NeedsUpdate
|
from authentik.outposts.controllers.k8s.triggers import NeedsUpdate
|
||||||
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
||||||
from authentik.outposts.tasks import outpost_connection_discovery
|
from authentik.outposts.tasks import outpost_connection_discovery
|
||||||
@ -92,35 +91,6 @@ class OutpostKubernetesTests(TestCase):
|
|||||||
|
|
||||||
deployment_reconciler.delete(deployment_reconciler.get_reference_object())
|
deployment_reconciler.delete(deployment_reconciler.get_reference_object())
|
||||||
|
|
||||||
@pytest.mark.timeout(120)
|
|
||||||
def test_service_reconciler(self):
|
|
||||||
"""test that service requires update"""
|
|
||||||
controller = ProxyKubernetesController(self.outpost, self.service_connection)
|
|
||||||
service_reconciler = ServiceReconciler(controller)
|
|
||||||
|
|
||||||
self.assertIsNotNone(service_reconciler.retrieve())
|
|
||||||
|
|
||||||
config = self.outpost.config
|
|
||||||
config.kubernetes_service_type = "NodePort"
|
|
||||||
config.kubernetes_json_patches = {
|
|
||||||
"service": [
|
|
||||||
{
|
|
||||||
"op": "add",
|
|
||||||
"path": "/spec/ipFamilyPolicy",
|
|
||||||
"value": "PreferDualStack",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
self.outpost.config = config
|
|
||||||
|
|
||||||
with self.assertRaises(NeedsUpdate):
|
|
||||||
service_reconciler.reconcile(
|
|
||||||
service_reconciler.retrieve(),
|
|
||||||
service_reconciler.get_reference_object(),
|
|
||||||
)
|
|
||||||
|
|
||||||
service_reconciler.delete(service_reconciler.get_reference_object())
|
|
||||||
|
|
||||||
@pytest.mark.timeout(120)
|
@pytest.mark.timeout(120)
|
||||||
def test_controller_rename(self):
|
def test_controller_rename(self):
|
||||||
"""test that objects get deleted and re-created with new names"""
|
"""test that objects get deleted and re-created with new names"""
|
||||||
|
|||||||
@ -26,6 +26,12 @@ class TestProxyKubernetes(TestCase):
|
|||||||
outpost_connection_discovery()
|
outpost_connection_discovery()
|
||||||
self.controller = None
|
self.controller = None
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
if self.controller:
|
||||||
|
for log in self.controller.down_with_logs():
|
||||||
|
LOGGER.info(log)
|
||||||
|
return super().tearDown()
|
||||||
|
|
||||||
@pytest.mark.timeout(120)
|
@pytest.mark.timeout(120)
|
||||||
def test_kubernetes_controller_static(self):
|
def test_kubernetes_controller_static(self):
|
||||||
"""Test Kubernetes Controller"""
|
"""Test Kubernetes Controller"""
|
||||||
|
|||||||
269
tests/wdio/package-lock.json
generated
269
tests/wdio/package-lock.json
generated
@ -6,23 +6,23 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "@goauthentik/web-tests",
|
"name": "@goauthentik/web-tests",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chromedriver": "^123.0.1"
|
"chromedriver": "^122.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
"@wdio/cli": "^8.35.1",
|
"@wdio/cli": "^8.33.1",
|
||||||
"@wdio/local-runner": "^8.35.1",
|
"@wdio/local-runner": "^8.33.1",
|
||||||
"@wdio/mocha-framework": "^8.35.0",
|
"@wdio/mocha-framework": "^8.33.1",
|
||||||
"@wdio/spec-reporter": "^8.32.4",
|
"@wdio/spec-reporter": "^8.32.4",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-sonarjs": "^0.25.0",
|
"eslint-plugin-sonarjs": "^0.24.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.4.3",
|
"typescript": "^5.4.2",
|
||||||
"wdio-wait-for": "^3.0.11"
|
"wdio-wait-for": "^3.0.11"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -889,13 +889,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.11.28",
|
"version": "20.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.0.tgz",
|
||||||
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
|
"integrity": "sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==",
|
||||||
"devOptional": true,
|
"devOptional": true
|
||||||
"dependencies": {
|
|
||||||
"undici-types": "~5.26.4"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/normalize-package-data": {
|
"node_modules/@types/normalize-package-data": {
|
||||||
"version": "2.4.4",
|
"version": "2.4.4",
|
||||||
@ -955,16 +952,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz",
|
||||||
"integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==",
|
"integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.5.1",
|
"@eslint-community/regexpp": "^4.5.1",
|
||||||
"@typescript-eslint/scope-manager": "7.4.0",
|
"@typescript-eslint/scope-manager": "7.2.0",
|
||||||
"@typescript-eslint/type-utils": "7.4.0",
|
"@typescript-eslint/type-utils": "7.2.0",
|
||||||
"@typescript-eslint/utils": "7.4.0",
|
"@typescript-eslint/utils": "7.2.0",
|
||||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
"@typescript-eslint/visitor-keys": "7.2.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.2.4",
|
"ignore": "^5.2.4",
|
||||||
@ -973,7 +970,7 @@
|
|||||||
"ts-api-utils": "^1.0.1"
|
"ts-api-utils": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -990,19 +987,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
|
||||||
"integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==",
|
"integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "7.4.0",
|
"@typescript-eslint/scope-manager": "7.2.0",
|
||||||
"@typescript-eslint/types": "7.4.0",
|
"@typescript-eslint/types": "7.2.0",
|
||||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
"@typescript-eslint/typescript-estree": "7.2.0",
|
||||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
"@typescript-eslint/visitor-keys": "7.2.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1018,16 +1015,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
|
||||||
"integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==",
|
"integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "7.4.0",
|
"@typescript-eslint/types": "7.2.0",
|
||||||
"@typescript-eslint/visitor-keys": "7.4.0"
|
"@typescript-eslint/visitor-keys": "7.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1035,18 +1032,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz",
|
||||||
"integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==",
|
"integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
"@typescript-eslint/typescript-estree": "7.2.0",
|
||||||
"@typescript-eslint/utils": "7.4.0",
|
"@typescript-eslint/utils": "7.2.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^1.0.1"
|
"ts-api-utils": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1062,12 +1059,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
|
||||||
"integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==",
|
"integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1075,13 +1072,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
|
||||||
"integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==",
|
"integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "7.4.0",
|
"@typescript-eslint/types": "7.2.0",
|
||||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
"@typescript-eslint/visitor-keys": "7.2.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@ -1090,7 +1087,7 @@
|
|||||||
"ts-api-utils": "^1.0.1"
|
"ts-api-utils": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1127,21 +1124,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz",
|
||||||
"integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==",
|
"integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@types/json-schema": "^7.0.12",
|
"@types/json-schema": "^7.0.12",
|
||||||
"@types/semver": "^7.5.0",
|
"@types/semver": "^7.5.0",
|
||||||
"@typescript-eslint/scope-manager": "7.4.0",
|
"@typescript-eslint/scope-manager": "7.2.0",
|
||||||
"@typescript-eslint/types": "7.4.0",
|
"@typescript-eslint/types": "7.2.0",
|
||||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
"@typescript-eslint/typescript-estree": "7.2.0",
|
||||||
"semver": "^7.5.4"
|
"semver": "^7.5.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1152,16 +1149,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "7.4.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
|
||||||
"integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==",
|
"integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "7.4.0",
|
"@typescript-eslint/types": "7.2.0",
|
||||||
"eslint-visitor-keys": "^3.4.1"
|
"eslint-visitor-keys": "^3.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || >=20.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1189,19 +1186,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/cli": {
|
"node_modules/@wdio/cli": {
|
||||||
"version": "8.35.1",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.33.1.tgz",
|
||||||
"integrity": "sha512-cdFmd6P/eQJdP2lChQ+Fa9b1c2p0bDIPmetVHGCuHiW8ZPkanrvBFtHMUhMu44a1koni9LvN/hu7vIJ/aAC+Rg==",
|
"integrity": "sha512-Ngt5R6YAmErkSKnWLWt1JilLIKDPIB0P93bzQhb9bQhmg1arFBcl75uiwe6kf6T355vzcNslMaEJyeuqGChmCg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.1",
|
"@types/node": "^20.1.1",
|
||||||
"@vitest/snapshot": "^1.2.1",
|
"@vitest/snapshot": "^1.2.1",
|
||||||
"@wdio/config": "8.35.0",
|
"@wdio/config": "8.33.1",
|
||||||
"@wdio/globals": "8.35.1",
|
"@wdio/globals": "8.33.1",
|
||||||
"@wdio/logger": "8.28.0",
|
"@wdio/logger": "8.28.0",
|
||||||
"@wdio/protocols": "8.32.0",
|
"@wdio/protocols": "8.32.0",
|
||||||
"@wdio/types": "8.32.4",
|
"@wdio/types": "8.32.4",
|
||||||
"@wdio/utils": "8.35.0",
|
"@wdio/utils": "8.33.1",
|
||||||
"async-exit-hook": "^2.0.1",
|
"async-exit-hook": "^2.0.1",
|
||||||
"chalk": "^5.2.0",
|
"chalk": "^5.2.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
@ -1216,7 +1213,7 @@
|
|||||||
"lodash.union": "^4.6.0",
|
"lodash.union": "^4.6.0",
|
||||||
"read-pkg-up": "10.0.0",
|
"read-pkg-up": "10.0.0",
|
||||||
"recursive-readdir": "^2.2.3",
|
"recursive-readdir": "^2.2.3",
|
||||||
"webdriverio": "8.35.1",
|
"webdriverio": "8.33.1",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -1239,14 +1236,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/config": {
|
"node_modules/@wdio/config": {
|
||||||
"version": "8.35.0",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.33.1.tgz",
|
||||||
"integrity": "sha512-I36sBPMl/+LCyQ3Pwb8gGQM6KxwmUfhOPp16TxN21Qo/Bc0fZfyGIg6KevmRu4DuqpGUm5MMVSfyPhLUkMk3Cg==",
|
"integrity": "sha512-JB7+tRkEsDJ4QAgJIZ3AaZvlp8pfBH6A5cKcGsaOuLVYMnsRPVkEGQc6n2akN9EPlDA2UjyrPOX6KZHbsSty7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wdio/logger": "8.28.0",
|
"@wdio/logger": "8.28.0",
|
||||||
"@wdio/types": "8.32.4",
|
"@wdio/types": "8.32.4",
|
||||||
"@wdio/utils": "8.35.0",
|
"@wdio/utils": "8.33.1",
|
||||||
"decamelize": "^6.0.0",
|
"decamelize": "^6.0.0",
|
||||||
"deepmerge-ts": "^5.0.0",
|
"deepmerge-ts": "^5.0.0",
|
||||||
"glob": "^10.2.2",
|
"glob": "^10.2.2",
|
||||||
@ -1257,28 +1254,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/globals": {
|
"node_modules/@wdio/globals": {
|
||||||
"version": "8.35.1",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.33.1.tgz",
|
||||||
"integrity": "sha512-T3IUFcKXRU9WWleAV72DGFWUiXSSr8SBvpc2cUJrvZ5Je9R2gEsrts5eHCY7amXtfeylfMgy5EayGMajgcna6A==",
|
"integrity": "sha512-1ud9oq7n9MMNywS/FoMRRWqW6uhcoxgnpXoGeLE2Tr+4f937ABOl+sfZgjycXujyvR7yTL8AROOYajp1Yuv1Xg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"expect-webdriverio": "^4.11.2",
|
"expect-webdriverio": "^4.11.2",
|
||||||
"webdriverio": "8.35.1"
|
"webdriverio": "8.33.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/local-runner": {
|
"node_modules/@wdio/local-runner": {
|
||||||
"version": "8.35.1",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.33.1.tgz",
|
||||||
"integrity": "sha512-PG+bADoY5VoWPmAfRi030rtxbFj68MVPlcwEN0dN1lDdYKz1ATzzGUK12sqCgGz1ktcC7sQzmJZVBklzbvn3mQ==",
|
"integrity": "sha512-eQp12wHIkyh5zl9fun1qjv5Qvf4mCHPgLs5sKbfo3OK4LadzmD4/QNvDG8DYq/9cyuhVvnHgbLQ3XAnkoPde3w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/logger": "8.28.0",
|
"@wdio/logger": "8.28.0",
|
||||||
"@wdio/repl": "8.24.12",
|
"@wdio/repl": "8.24.12",
|
||||||
"@wdio/runner": "8.35.1",
|
"@wdio/runner": "8.33.1",
|
||||||
"@wdio/types": "8.32.4",
|
"@wdio/types": "8.32.4",
|
||||||
"async-exit-hook": "^2.0.1",
|
"async-exit-hook": "^2.0.1",
|
||||||
"split2": "^4.1.0",
|
"split2": "^4.1.0",
|
||||||
@ -1316,16 +1313,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/mocha-framework": {
|
"node_modules/@wdio/mocha-framework": {
|
||||||
"version": "8.35.0",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.33.1.tgz",
|
||||||
"integrity": "sha512-riO3aMgvGdFFRMpyMk5m480V+mi5EcKk6cjT1TB9L5XEN7Mo/8qthBw9CLgFCZkr4KlR40hgPKSZFHE0rH2GpQ==",
|
"integrity": "sha512-CxYLE22+tgnMnruElvDGJGR+dE0pxvMZ95agIUYYen69DJ705a74XtTR6zX9COWu6RooBezHgEs3fXev0XL79Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mocha": "^10.0.0",
|
"@types/mocha": "^10.0.0",
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/logger": "8.28.0",
|
"@wdio/logger": "8.28.0",
|
||||||
"@wdio/types": "8.32.4",
|
"@wdio/types": "8.32.4",
|
||||||
"@wdio/utils": "8.35.0",
|
"@wdio/utils": "8.33.1",
|
||||||
"mocha": "^10.0.0"
|
"mocha": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1367,22 +1364,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/runner": {
|
"node_modules/@wdio/runner": {
|
||||||
"version": "8.35.1",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.33.1.tgz",
|
||||||
"integrity": "sha512-5F6cbOYeZjF34Vsnycp5JPnDljI52fmyxsV2O/L3h6F2+83YXpbsqBplw/2G24JtIUudV7VOY/38bUicn1OyXg==",
|
"integrity": "sha512-i0eRwMCePKkQocWsdkPQpBb1jELyNR5JCwnmOgM3g9fQI6KAf5D4oEUkNDFL/vD4UtgbSRmux7b7j5G01VvuqQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.11.28",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/config": "8.35.0",
|
"@wdio/config": "8.33.1",
|
||||||
"@wdio/globals": "8.35.1",
|
"@wdio/globals": "8.33.1",
|
||||||
"@wdio/logger": "8.28.0",
|
"@wdio/logger": "8.28.0",
|
||||||
"@wdio/types": "8.32.4",
|
"@wdio/types": "8.32.4",
|
||||||
"@wdio/utils": "8.35.0",
|
"@wdio/utils": "8.33.1",
|
||||||
"deepmerge-ts": "^5.1.0",
|
"deepmerge-ts": "^5.0.0",
|
||||||
"expect-webdriverio": "^4.12.0",
|
"expect-webdriverio": "^4.11.2",
|
||||||
"gaze": "^1.1.3",
|
"gaze": "^1.1.2",
|
||||||
"webdriver": "8.35.0",
|
"webdriver": "8.33.1",
|
||||||
"webdriverio": "8.35.1"
|
"webdriverio": "8.33.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
@ -1429,9 +1426,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/utils": {
|
"node_modules/@wdio/utils": {
|
||||||
"version": "8.35.0",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.33.1.tgz",
|
||||||
"integrity": "sha512-9KCyn4aS+9tWfthnUkNFVe52AM6QrLGAeIxgGxNlzTAcQGl7jjwdDM7aSK0RjLkWI3a/88DRH21mN/t2LGDmPQ==",
|
"integrity": "sha512-W0ArrZbs4M23POv8+FPsgHDFxg+wwklfZgLSsjVq2kpCmBCfIPxKSAOgTo/XrcH4We/OnshgBzxLcI+BHDgi4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@puppeteer/browsers": "^1.6.0",
|
"@puppeteer/browsers": "^1.6.0",
|
||||||
@ -2084,9 +2081,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chromedriver": {
|
"node_modules/chromedriver": {
|
||||||
"version": "123.0.1",
|
"version": "122.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-122.0.6.tgz",
|
||||||
"integrity": "sha512-YQUIP/zdlzDIRCZNCv6rEVDSY4RAxo/tDL0OiGPPuai+z8unRNqJr/9V6XTBypVFyDheXNalKt9QxEqdMPuLAQ==",
|
"integrity": "sha512-Q0r+QlUtiJWMQ5HdYaFa0CtBmLFq3n5JWfmq9mOC00UMBvWxku09gUkvBt457QnYfTM/XHqY/HTFOxHvATnTmA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@testim/chrome-version": "^1.1.4",
|
"@testim/chrome-version": "^1.1.4",
|
||||||
@ -2691,9 +2688,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/devtools-protocol": {
|
"node_modules/devtools-protocol": {
|
||||||
"version": "0.0.1273771",
|
"version": "0.0.1263784",
|
||||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1273771.tgz",
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1263784.tgz",
|
||||||
"integrity": "sha512-QDbb27xcTVReQQW/GHJsdQqGKwYBE7re7gxehj467kKP2DKuYBUj6i2k5LRiAC66J1yZG/9gsxooz/s9pcm0Og==",
|
"integrity": "sha512-k0SCZMwj587w4F8QYbP5iIbSonL6sd3q8aVJch036r9Tv2t9b5/Oq7AiJ/FJvRuORm/pJNXZtrdNNWlpRnl56A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
@ -3114,9 +3111,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-sonarjs": {
|
"node_modules/eslint-plugin-sonarjs": {
|
||||||
"version": "0.25.0",
|
"version": "0.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.24.0.tgz",
|
||||||
"integrity": "sha512-DaZOtpUucEZbvowgKxVFwICV6r0h7jSCAx0IHICvCowP+etFussnhtaiCPSnYAuwVJ+P/6UFUhkv7QJklpXFyA==",
|
"integrity": "sha512-87zp50mbbNrSTuoEOebdRQBPa0mdejA5UEjyuScyIw8hEpEjfWP89Qhkq5xVZfVyVSRQKZc9alVm7yRKQvvUmg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
@ -3413,9 +3410,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expect-webdriverio": {
|
"node_modules/expect-webdriverio": {
|
||||||
"version": "4.12.1",
|
"version": "4.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.11.2.tgz",
|
||||||
"integrity": "sha512-jTfyC2bJbPNw4c8MlEwZNX7SjtPbZ73ysJvr/OGKA9mSKC+toyjU2eMNzHlt9WZO5+wl0RDS1dR7VxHXeu7+zA==",
|
"integrity": "sha512-PK8lrQmRhK8NRtE8i/CJsnKS/QGrHULQW5EfmyKxIiLHnXd7t8dX0dWJn/fiYVtrPKBUyXSL6h52QqvZVc9yGQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/snapshot": "^1.2.2",
|
"@vitest/snapshot": "^1.2.2",
|
||||||
@ -3699,9 +3696,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.6",
|
"version": "1.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@ -8620,9 +8617,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.4.3",
|
"version": "5.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
|
||||||
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
|
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@ -8657,12 +8654,6 @@
|
|||||||
"through": "^2.3.8"
|
"through": "^2.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
|
||||||
"version": "5.26.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
|
||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
|
||||||
"devOptional": true
|
|
||||||
},
|
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
@ -8886,18 +8877,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webdriver": {
|
"node_modules/webdriver": {
|
||||||
"version": "8.35.0",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.33.1.tgz",
|
||||||
"integrity": "sha512-D13EroddIXDqdq3jgO8j6sorgTWqTwEiTqwlDoJizpRIgHGBy+UjkNM7XW1yVcvt8gsD2Dei2LQth2tJEnu5Ng==",
|
"integrity": "sha512-QREF4c08omN9BPh3QDmz5h+OEvjdzDliuEcrDuXoDnHSMxIj1rsonzsgRaM2PXhFZuPeMIiKZYqc7Qg9BGbh6A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@types/ws": "^8.5.3",
|
"@types/ws": "^8.5.3",
|
||||||
"@wdio/config": "8.35.0",
|
"@wdio/config": "8.33.1",
|
||||||
"@wdio/logger": "8.28.0",
|
"@wdio/logger": "8.28.0",
|
||||||
"@wdio/protocols": "8.32.0",
|
"@wdio/protocols": "8.32.0",
|
||||||
"@wdio/types": "8.32.4",
|
"@wdio/types": "8.32.4",
|
||||||
"@wdio/utils": "8.35.0",
|
"@wdio/utils": "8.33.1",
|
||||||
"deepmerge-ts": "^5.1.0",
|
"deepmerge-ts": "^5.1.0",
|
||||||
"got": "^12.6.1",
|
"got": "^12.6.1",
|
||||||
"ky": "^0.33.0",
|
"ky": "^0.33.0",
|
||||||
@ -8908,23 +8899,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webdriverio": {
|
"node_modules/webdriverio": {
|
||||||
"version": "8.35.1",
|
"version": "8.33.1",
|
||||||
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.35.1.tgz",
|
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.33.1.tgz",
|
||||||
"integrity": "sha512-YAuKR4JERGiMqCJmm5fEVZ160iiFPyupwALqfXfzrYVcEmKltKPFY/oUCArmi6Uzqd+Sa2Kp9WZtz2Eu1R76JA==",
|
"integrity": "sha512-1DsF8sx1a46AoVYCUpEwJYU74iBAW/U2H5r6p+60ct7dIiFmxmc4uCbOqtf7NLOTgrIzAOaRnT0EsrRICpg5Qw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/config": "8.35.0",
|
"@wdio/config": "8.33.1",
|
||||||
"@wdio/logger": "8.28.0",
|
"@wdio/logger": "8.28.0",
|
||||||
"@wdio/protocols": "8.32.0",
|
"@wdio/protocols": "8.32.0",
|
||||||
"@wdio/repl": "8.24.12",
|
"@wdio/repl": "8.24.12",
|
||||||
"@wdio/types": "8.32.4",
|
"@wdio/types": "8.32.4",
|
||||||
"@wdio/utils": "8.35.0",
|
"@wdio/utils": "8.33.1",
|
||||||
"archiver": "^7.0.0",
|
"archiver": "^7.0.0",
|
||||||
"aria-query": "^5.0.0",
|
"aria-query": "^5.0.0",
|
||||||
"css-shorthand-properties": "^1.1.1",
|
"css-shorthand-properties": "^1.1.1",
|
||||||
"css-value": "^0.0.1",
|
"css-value": "^0.0.1",
|
||||||
"devtools-protocol": "^0.0.1273771",
|
"devtools-protocol": "^0.0.1263784",
|
||||||
"grapheme-splitter": "^1.0.2",
|
"grapheme-splitter": "^1.0.2",
|
||||||
"import-meta-resolve": "^4.0.0",
|
"import-meta-resolve": "^4.0.0",
|
||||||
"is-plain-obj": "^4.1.0",
|
"is-plain-obj": "^4.1.0",
|
||||||
@ -8936,7 +8927,7 @@
|
|||||||
"resq": "^1.9.1",
|
"resq": "^1.9.1",
|
||||||
"rgb2hex": "0.2.5",
|
"rgb2hex": "0.2.5",
|
||||||
"serialize-error": "^11.0.1",
|
"serialize-error": "^11.0.1",
|
||||||
"webdriver": "8.35.0"
|
"webdriver": "8.33.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
|
|||||||
@ -4,19 +4,19 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
"@wdio/cli": "^8.35.1",
|
"@wdio/cli": "^8.33.1",
|
||||||
"@wdio/local-runner": "^8.35.1",
|
"@wdio/local-runner": "^8.33.1",
|
||||||
"@wdio/mocha-framework": "^8.35.0",
|
"@wdio/mocha-framework": "^8.33.1",
|
||||||
"@wdio/spec-reporter": "^8.32.4",
|
"@wdio/spec-reporter": "^8.32.4",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-sonarjs": "^0.25.0",
|
"eslint-plugin-sonarjs": "^0.24.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.4.3",
|
"typescript": "^5.4.2",
|
||||||
"wdio-wait-for": "^3.0.11"
|
"wdio-wait-for": "^3.0.11"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -32,6 +32,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chromedriver": "^123.0.1"
|
"chromedriver": "^122.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,15 +55,15 @@ for (const [source, rawdest, strip] of otherFiles) {
|
|||||||
|
|
||||||
// This starts the definitions used for esbuild: Our targets, our arguments, the function for running a build, and three
|
// This starts the definitions used for esbuild: Our targets, our arguments, the function for running a build, and three
|
||||||
// options for building: watching, building, and building the proxy.
|
// options for building: watching, building, and building the proxy.
|
||||||
// Ordered by largest to smallest interface to build even faster
|
|
||||||
const interfaces = [
|
const interfaces = [
|
||||||
["admin/AdminInterface/AdminInterface.ts", "admin"],
|
|
||||||
["user/UserInterface.ts", "user"],
|
|
||||||
["flow/FlowInterface.ts", "flow"],
|
|
||||||
["standalone/api-browser/index.ts", "standalone/api-browser"],
|
|
||||||
["enterprise/rac/index.ts", "enterprise/rac"],
|
|
||||||
["standalone/loading/index.ts", "standalone/loading"],
|
|
||||||
["polyfill/poly.ts", "."],
|
["polyfill/poly.ts", "."],
|
||||||
|
["standalone/loading/index.ts", "standalone/loading"],
|
||||||
|
["flow/FlowInterface.ts", "flow"],
|
||||||
|
["user/UserInterface.ts", "user"],
|
||||||
|
["enterprise/rac/index.ts", "enterprise/rac"],
|
||||||
|
["standalone/api-browser/index.ts", "standalone/api-browser"],
|
||||||
|
["admin/AdminInterface/AdminInterface.ts", "admin"],
|
||||||
];
|
];
|
||||||
|
|
||||||
const baseArgs = {
|
const baseArgs = {
|
||||||
@ -80,32 +80,29 @@ const baseArgs = {
|
|||||||
format: "esm",
|
format: "esm",
|
||||||
};
|
};
|
||||||
|
|
||||||
async function buildOneSource(source, dest) {
|
function buildAuthentik(interfaces) {
|
||||||
const DIST = path.join(__dirname, "./dist", dest);
|
for (const [source, dest] of interfaces) {
|
||||||
console.log(`[${new Date(Date.now()).toISOString()}] Starting build for target ${source}`);
|
const DIST = path.join(__dirname, "./dist", dest);
|
||||||
|
console.log(`[${new Date(Date.now()).toISOString()}] Starting build for target ${source}`);
|
||||||
try {
|
try {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
await esbuild.build({
|
esbuild.buildSync({
|
||||||
...baseArgs,
|
...baseArgs,
|
||||||
entryPoints: [`./src/${source}`],
|
entryPoints: [`./src/${source}`],
|
||||||
outdir: DIST,
|
outdir: DIST,
|
||||||
});
|
});
|
||||||
const end = Date.now();
|
const end = Date.now();
|
||||||
console.log(
|
console.log(
|
||||||
`[${new Date(end).toISOString()}] Finished build for target ${source} in ${
|
`[${new Date(end).toISOString()}] Finished build for target ${source} in ${Date.now() - start}ms`,
|
||||||
Date.now() - start
|
);
|
||||||
}ms`,
|
} catch (exc) {
|
||||||
);
|
console.error(
|
||||||
} catch (exc) {
|
`[${new Date(Date.now()).toISOString()}] Failed to build ${source}: ${exc}`,
|
||||||
console.error(`[${new Date(Date.now()).toISOString()}] Failed to build ${source}: ${exc}`);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildAuthentik(interfaces) {
|
|
||||||
await Promise.allSettled(interfaces.map(([source, dest]) => buildOneSource(source, dest)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeoutId = null;
|
let timeoutId = null;
|
||||||
function debouncedBuild() {
|
function debouncedBuild() {
|
||||||
if (timeoutId !== null) {
|
if (timeoutId !== null) {
|
||||||
@ -141,11 +138,9 @@ if (process.argv.length > 2 && (process.argv[2] === "-w" || process.argv[2] ===
|
|||||||
});
|
});
|
||||||
} else if (process.argv.length > 2 && (process.argv[2] === "-p" || process.argv[2] === "--proxy")) {
|
} else if (process.argv.length > 2 && (process.argv[2] === "-p" || process.argv[2] === "--proxy")) {
|
||||||
// There's no watch-for-proxy, sorry.
|
// There's no watch-for-proxy, sorry.
|
||||||
await buildAuthentik(
|
buildAuthentik(interfaces.slice(0, 2));
|
||||||
interfaces.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)),
|
|
||||||
);
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
} else {
|
} else {
|
||||||
// And the fallback: just build it.
|
// And the fallback: just build it.
|
||||||
await buildAuthentik(interfaces);
|
buildAuthentik(interfaces);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
### 2024-03-26T09:25:06-0700
|
|
||||||
|
|
||||||
Split the tsconfig file into a base and build variant.
|
|
||||||
|
|
||||||
Lesson: This lesson is stored here and not in a comment in tsconfig.json because
|
|
||||||
JSON doesn't like comments. Doug Crockford's purity requirement has doomed an
|
|
||||||
entire generation to keeping its human-facing meta somewhere other than in the
|
|
||||||
file where it belongs.
|
|
||||||
|
|
||||||
Lesson: The `extend` command of tsconfig has an unexpected behavior. It is
|
|
||||||
neither a merge or a replace, but some mixture of the two. The buildfile's
|
|
||||||
`compilerOptions` is not a full replacement; instead, each of _its_ top-level
|
|
||||||
fields is a replacement for what is found in the basefile. So while you don't
|
|
||||||
need to include _everything_ in a `compilerOptions` field if you want to change
|
|
||||||
one thing, if you want to modify _one_ path in `compilerOptions.path`, you must
|
|
||||||
include the entire `compilerOptions.path` collection in your buildfile.
|
|
||||||
g
|
|
||||||
1299
web/package-lock.json
generated
1299
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,8 +14,8 @@
|
|||||||
"build": "run-s build-locales esbuild:build",
|
"build": "run-s build-locales esbuild:build",
|
||||||
"build-proxy": "run-s build-locales esbuild:build-proxy",
|
"build-proxy": "run-s build-locales esbuild:build-proxy",
|
||||||
"watch": "run-s build-locales esbuild:watch",
|
"watch": "run-s build-locales esbuild:watch",
|
||||||
"lint": "cross-env NODE_OPTIONS='--max_old_space_size=8192' eslint . --max-warnings 0 --fix",
|
"lint": "eslint . --max-warnings 0 --fix",
|
||||||
"lint:precommit": "cross-env NODE_OPTIONS='--max_old_space_size=8192' node scripts/eslint-precommit.mjs",
|
"lint:precommit": "node scripts/eslint-precommit.mjs",
|
||||||
"lint:spelling": "node scripts/check-spelling.mjs",
|
"lint:spelling": "node scripts/check-spelling.mjs",
|
||||||
"lit-analyse": "lit-analyzer src",
|
"lit-analyse": "lit-analyzer src",
|
||||||
"precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
|
"precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
|
||||||
@ -32,29 +32,29 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-html": "^6.4.8",
|
"@codemirror/lang-html": "^6.4.8",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/lang-python": "^6.1.5",
|
"@codemirror/lang-python": "^6.1.4",
|
||||||
"@codemirror/lang-xml": "^6.1.0",
|
"@codemirror/lang-xml": "^6.1.0",
|
||||||
"@codemirror/legacy-modes": "^6.3.3",
|
"@codemirror/legacy-modes": "^6.3.3",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@formatjs/intl-listformat": "^7.5.5",
|
"@formatjs/intl-listformat": "^7.5.5",
|
||||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||||
"@goauthentik/api": "^2024.2.2-1711643691",
|
"@goauthentik/api": "^2024.2.2-1709583949",
|
||||||
"@lit-labs/task": "^3.1.0",
|
"@lit-labs/task": "^3.1.0",
|
||||||
|
"@lit/reactive-element": "^2.0.4",
|
||||||
"@lit/context": "^1.1.0",
|
"@lit/context": "^1.1.0",
|
||||||
"@lit/localize": "^0.12.1",
|
"@lit/localize": "^0.12.1",
|
||||||
"@lit/reactive-element": "^2.0.4",
|
|
||||||
"@open-wc/lit-helpers": "^0.7.0",
|
"@open-wc/lit-helpers": "^0.7.0",
|
||||||
"@patternfly/elements": "^2.4.0",
|
"@patternfly/elements": "^2.4.0",
|
||||||
"@patternfly/patternfly": "^4.224.2",
|
"@patternfly/patternfly": "^4.224.2",
|
||||||
"@sentry/browser": "^7.109.0",
|
"@sentry/browser": "^7.106.1",
|
||||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"chart.js": "^4.4.2",
|
"chart.js": "^4.4.2",
|
||||||
"chartjs-adapter-moment": "^1.0.1",
|
"chartjs-adapter-moment": "^1.0.1",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"construct-style-sheets-polyfill": "^3.1.0",
|
"construct-style-sheets-polyfill": "^3.1.0",
|
||||||
"core-js": "^3.36.1",
|
"core-js": "^3.36.0",
|
||||||
"country-flag-icons": "^1.5.10",
|
"country-flag-icons": "^1.5.9",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"guacamole-common-js": "^1.5.0",
|
"guacamole-common-js": "^1.5.0",
|
||||||
"lit": "^3.1.2",
|
"lit": "^3.1.2",
|
||||||
@ -68,19 +68,18 @@
|
|||||||
"yaml": "^2.4.1"
|
"yaml": "^2.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.3",
|
"@babel/core": "^7.24.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-proposal-decorators": "^7.24.1",
|
"@babel/plugin-proposal-decorators": "^7.24.0",
|
||||||
"@babel/plugin-transform-private-methods": "^7.24.1",
|
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||||
"@babel/plugin-transform-private-property-in-object": "^7.24.1",
|
"@babel/plugin-transform-private-property-in-object": "^7.23.4",
|
||||||
"@babel/plugin-transform-runtime": "^7.24.3",
|
"@babel/plugin-transform-runtime": "^7.24.0",
|
||||||
"@babel/preset-env": "^7.24.3",
|
"@babel/preset-env": "^7.24.0",
|
||||||
"@babel/preset-typescript": "^7.24.1",
|
"@babel/preset-typescript": "^7.23.3",
|
||||||
"@hcaptcha/types": "^1.0.3",
|
"@hcaptcha/types": "^1.0.3",
|
||||||
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
|
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
|
||||||
"@lit/localize-tools": "^0.7.2",
|
"@lit/localize-tools": "^0.7.2",
|
||||||
"@rollup/plugin-replace": "^5.0.5",
|
"@spotlightjs/spotlight": "^1.2.12",
|
||||||
"@spotlightjs/spotlight": "^1.2.16",
|
|
||||||
"@storybook/addon-essentials": "^7.6.17",
|
"@storybook/addon-essentials": "^7.6.17",
|
||||||
"@storybook/addon-links": "^7.6.17",
|
"@storybook/addon-links": "^7.6.17",
|
||||||
"@storybook/api": "^7.6.17",
|
"@storybook/api": "^7.6.17",
|
||||||
@ -91,47 +90,48 @@
|
|||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@types/chart.js": "^2.9.41",
|
"@types/chart.js": "^2.9.41",
|
||||||
"@types/codemirror": "5.60.15",
|
"@types/codemirror": "5.60.15",
|
||||||
"@types/grecaptcha": "^3.0.9",
|
"@types/grecaptcha": "^3.0.8",
|
||||||
"@types/guacamole-common-js": "1.5.2",
|
"@types/guacamole-common-js": "1.5.2",
|
||||||
"@types/showdown": "^2.0.6",
|
"@types/showdown": "^2.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
|
"@rollup/plugin-replace": "^5.0.5",
|
||||||
|
"rollup-plugin-modify": "^3.0.0",
|
||||||
|
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esbuild": "^0.20.2",
|
"esbuild": "^0.20.1",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-custom-elements": "0.0.8",
|
"eslint-plugin-custom-elements": "0.0.8",
|
||||||
"eslint-plugin-lit": "^1.11.0",
|
"eslint-plugin-lit": "^1.11.0",
|
||||||
"eslint-plugin-sonarjs": "^0.25.0",
|
"eslint-plugin-sonarjs": "^0.24.0",
|
||||||
"eslint-plugin-storybook": "^0.8.0",
|
"eslint-plugin-storybook": "^0.8.0",
|
||||||
"github-slugger": "^2.0.0",
|
"github-slugger": "^2.0.0",
|
||||||
"glob": "^10.3.12",
|
"glob": "^10.3.10",
|
||||||
"lit-analyzer": "^2.0.3",
|
"lit-analyzer": "^2.0.3",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"pseudolocale": "^2.0.0",
|
"pseudolocale": "^2.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"rollup-plugin-modify": "^3.0.0",
|
|
||||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
|
||||||
"storybook": "^7.6.17",
|
"storybook": "^7.6.17",
|
||||||
"storybook-addon-mock": "^4.3.0",
|
"storybook-addon-mock": "^4.3.0",
|
||||||
"ts-lit-plugin": "^2.0.2",
|
"ts-lit-plugin": "^2.0.2",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"turnstile-types": "^1.2.0",
|
"turnstile-types": "^1.2.0",
|
||||||
"typescript": "^5.4.3",
|
"typescript": "^5.4.2",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/darwin-arm64": "^0.20.1",
|
"@esbuild/darwin-arm64": "^0.20.1",
|
||||||
"@esbuild/linux-amd64": "^0.18.11",
|
"@esbuild/linux-amd64": "^0.18.11",
|
||||||
"@esbuild/linux-arm64": "^0.20.1",
|
"@esbuild/linux-arm64": "^0.20.1",
|
||||||
"@rollup/rollup-darwin-arm64": "4.13.2",
|
"@rollup/rollup-darwin-arm64": "4.13.0",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.13.2",
|
"@rollup/rollup-linux-arm64-gnu": "4.13.0",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.13.2"
|
"@rollup/rollup-linux-x64-gnu": "4.13.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
|
|||||||
@ -1,155 +1,266 @@
|
|||||||
import "@goauthentik/admin/admin-overview/AdminOverviewPage";
|
import "@goauthentik/admin/admin-overview/AdminOverviewPage";
|
||||||
import { ID_REGEX, Route, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
||||||
|
import { RawRoute, makeRoute } from "@goauthentik/elements/router/routeUtils";
|
||||||
|
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
|
|
||||||
export const ROUTES: Route[] = [
|
export const _ROUTES: RawRoute[] = [
|
||||||
// Prevent infinite Shell loops
|
// Prevent infinite Shell loops
|
||||||
new Route(new RegExp("^/$")).redirect("/administration/overview"),
|
["^/$", "/administration/overview"],
|
||||||
new Route(new RegExp("^#.*")).redirect("/administration/overview"),
|
["^#.*", "/administration/overview"],
|
||||||
new Route(new RegExp("^/library$")).redirect("/if/user/", true),
|
["^/library$", ["/if/user/", true]],
|
||||||
// statically imported since this is the default route
|
// statically imported since this is the default route
|
||||||
new Route(new RegExp("^/administration/overview$"), async () => {
|
[
|
||||||
return html`<ak-admin-overview></ak-admin-overview>`;
|
"^/administration/overview$",
|
||||||
}),
|
async () => {
|
||||||
new Route(new RegExp("^/administration/dashboard/users$"), async () => {
|
return html`<ak-admin-overview></ak-admin-overview>`;
|
||||||
await import("@goauthentik/admin/admin-overview/DashboardUserPage");
|
},
|
||||||
return html`<ak-admin-dashboard-users></ak-admin-dashboard-users>`;
|
],
|
||||||
}),
|
[
|
||||||
new Route(new RegExp("^/administration/system-tasks$"), async () => {
|
"^/administration/dashboard/users$",
|
||||||
await import("@goauthentik/admin/system-tasks/SystemTaskListPage");
|
async () => {
|
||||||
return html`<ak-system-task-list></ak-system-task-list>`;
|
await import("@goauthentik/admin/admin-overview/DashboardUserPage");
|
||||||
}),
|
return html`<ak-admin-dashboard-users></ak-admin-dashboard-users>`;
|
||||||
new Route(new RegExp("^/core/providers$"), async () => {
|
},
|
||||||
await import("@goauthentik/admin/providers/ProviderListPage");
|
],
|
||||||
return html`<ak-provider-list></ak-provider-list>`;
|
[
|
||||||
}),
|
"^/administration/system-tasks$",
|
||||||
new Route(new RegExp(`^/core/providers/(?<id>${ID_REGEX})$`), async (args) => {
|
async () => {
|
||||||
await import("@goauthentik/admin/providers/ProviderViewPage");
|
await import("@goauthentik/admin/system-tasks/SystemTaskListPage");
|
||||||
return html`<ak-provider-view .providerID=${parseInt(args.id, 10)}></ak-provider-view>`;
|
return html`<ak-system-task-list></ak-system-task-list>`;
|
||||||
}),
|
},
|
||||||
new Route(new RegExp("^/core/applications$"), async () => {
|
],
|
||||||
await import("@goauthentik/admin/applications/ApplicationListPage");
|
[
|
||||||
return html`<ak-application-list></ak-application-list>`;
|
"^/core/providers$",
|
||||||
}),
|
async () => {
|
||||||
new Route(new RegExp(`^/core/applications/(?<slug>${SLUG_REGEX})$`), async (args) => {
|
await import("@goauthentik/admin/providers/ProviderListPage");
|
||||||
await import("@goauthentik/admin/applications/ApplicationViewPage");
|
return html`<ak-provider-list></ak-provider-list>`;
|
||||||
return html`<ak-application-view .applicationSlug=${args.slug}></ak-application-view>`;
|
},
|
||||||
}),
|
],
|
||||||
new Route(new RegExp("^/core/sources$"), async () => {
|
[
|
||||||
await import("@goauthentik/admin/sources/SourceListPage");
|
`^/core/providers/(?<id>${ID_REGEX}])$`,
|
||||||
return html`<ak-source-list></ak-source-list>`;
|
async (args) => {
|
||||||
}),
|
await import("@goauthentik/admin/providers/ProviderViewPage");
|
||||||
new Route(new RegExp(`^/core/sources/(?<slug>${SLUG_REGEX})$`), async (args) => {
|
return html`<ak-provider-view .providerID=${parseInt(args.id, 10)}></ak-provider-view>`;
|
||||||
await import("@goauthentik/admin/sources/SourceViewPage");
|
},
|
||||||
return html`<ak-source-view .sourceSlug=${args.slug}></ak-source-view>`;
|
],
|
||||||
}),
|
[
|
||||||
new Route(new RegExp("^/core/property-mappings$"), async () => {
|
"^/core/applications$",
|
||||||
await import("@goauthentik/admin/property-mappings/PropertyMappingListPage");
|
async () => {
|
||||||
return html`<ak-property-mapping-list></ak-property-mapping-list>`;
|
await import("@goauthentik/admin/applications/ApplicationListPage");
|
||||||
}),
|
return html`<ak-application-list></ak-application-list>`;
|
||||||
new Route(new RegExp("^/core/tokens$"), async () => {
|
},
|
||||||
await import("@goauthentik/admin/tokens/TokenListPage");
|
],
|
||||||
return html`<ak-token-list></ak-token-list>`;
|
[
|
||||||
}),
|
`^/core/applications/(?<slug>${SLUG_REGEX})$`,
|
||||||
new Route(new RegExp("^/core/brands"), async () => {
|
async (args) => {
|
||||||
await import("@goauthentik/admin/brands/BrandListPage");
|
await import("@goauthentik/admin/applications/ApplicationViewPage");
|
||||||
return html`<ak-brand-list></ak-brand-list>`;
|
return html`<ak-application-view .applicationSlug=${args.slug}></ak-application-view>`;
|
||||||
}),
|
},
|
||||||
new Route(new RegExp("^/policy/policies$"), async () => {
|
],
|
||||||
await import("@goauthentik/admin/policies/PolicyListPage");
|
[
|
||||||
return html`<ak-policy-list></ak-policy-list>`;
|
"^/core/sources$",
|
||||||
}),
|
async () => {
|
||||||
new Route(new RegExp("^/policy/reputation$"), async () => {
|
await import("@goauthentik/admin/sources/SourceListPage");
|
||||||
await import("@goauthentik/admin/policies/reputation/ReputationListPage");
|
return html`<ak-source-list></ak-source-list>`;
|
||||||
return html`<ak-policy-reputation-list></ak-policy-reputation-list>`;
|
},
|
||||||
}),
|
],
|
||||||
new Route(new RegExp("^/identity/groups$"), async () => {
|
[
|
||||||
await import("@goauthentik/admin/groups/GroupListPage");
|
`^/core/sources/(?<slug>${SLUG_REGEX})$`,
|
||||||
return html`<ak-group-list></ak-group-list>`;
|
async (args) => {
|
||||||
}),
|
await import("@goauthentik/admin/sources/SourceViewPage");
|
||||||
new Route(new RegExp(`^/identity/groups/(?<uuid>${UUID_REGEX})$`), async (args) => {
|
return html`<ak-source-view .sourceSlug=${args.slug}></ak-source-view>`;
|
||||||
await import("@goauthentik/admin/groups/GroupViewPage");
|
},
|
||||||
return html`<ak-group-view .groupId=${args.uuid}></ak-group-view>`;
|
],
|
||||||
}),
|
[
|
||||||
new Route(new RegExp("^/identity/users$"), async () => {
|
"^/core/property-mappings$",
|
||||||
await import("@goauthentik/admin/users/UserListPage");
|
async () => {
|
||||||
return html`<ak-user-list></ak-user-list>`;
|
await import("@goauthentik/admin/property-mappings/PropertyMappingListPage");
|
||||||
}),
|
return html`<ak-property-mapping-list></ak-property-mapping-list>`;
|
||||||
new Route(new RegExp(`^/identity/users/(?<id>${ID_REGEX})$`), async (args) => {
|
},
|
||||||
await import("@goauthentik/admin/users/UserViewPage");
|
],
|
||||||
return html`<ak-user-view .userId=${parseInt(args.id, 10)}></ak-user-view>`;
|
[
|
||||||
}),
|
"^/core/tokens$",
|
||||||
new Route(new RegExp("^/identity/roles$"), async () => {
|
async () => {
|
||||||
await import("@goauthentik/admin/roles/RoleListPage");
|
await import("@goauthentik/admin/tokens/TokenListPage");
|
||||||
return html`<ak-role-list></ak-role-list>`;
|
return html`<ak-token-list></ak-token-list>`;
|
||||||
}),
|
},
|
||||||
new Route(new RegExp(`^/identity/roles/(?<id>${UUID_REGEX})$`), async (args) => {
|
],
|
||||||
await import("@goauthentik/admin/roles/RoleViewPage");
|
[
|
||||||
return html`<ak-role-view roleId=${args.id}></ak-role-view>`;
|
"^/core/brands",
|
||||||
}),
|
async () => {
|
||||||
new Route(new RegExp("^/flow/stages/invitations$"), async () => {
|
await import("@goauthentik/admin/brands/BrandListPage");
|
||||||
await import("@goauthentik/admin/stages/invitation/InvitationListPage");
|
return html`<ak-brand-list></ak-brand-list>`;
|
||||||
return html`<ak-stage-invitation-list></ak-stage-invitation-list>`;
|
},
|
||||||
}),
|
],
|
||||||
new Route(new RegExp("^/flow/stages/prompts$"), async () => {
|
[
|
||||||
await import("@goauthentik/admin/stages/prompt/PromptListPage");
|
"^/policy/policies$",
|
||||||
return html`<ak-stage-prompt-list></ak-stage-prompt-list>`;
|
async () => {
|
||||||
}),
|
await import("@goauthentik/admin/policies/PolicyListPage");
|
||||||
new Route(new RegExp("^/flow/stages$"), async () => {
|
return html`<ak-policy-list></ak-policy-list>`;
|
||||||
await import("@goauthentik/admin/stages/StageListPage");
|
},
|
||||||
return html`<ak-stage-list></ak-stage-list>`;
|
],
|
||||||
}),
|
[
|
||||||
new Route(new RegExp("^/flow/flows$"), async () => {
|
"^/policy/reputation$",
|
||||||
await import("@goauthentik/admin/flows/FlowListPage");
|
async () => {
|
||||||
return html`<ak-flow-list></ak-flow-list>`;
|
await import("@goauthentik/admin/policies/reputation/ReputationListPage");
|
||||||
}),
|
return html`<ak-policy-reputation-list></ak-policy-reputation-list>`;
|
||||||
new Route(new RegExp(`^/flow/flows/(?<slug>${SLUG_REGEX})$`), async (args) => {
|
},
|
||||||
await import("@goauthentik/admin/flows/FlowViewPage");
|
],
|
||||||
return html`<ak-flow-view .flowSlug=${args.slug}></ak-flow-view>`;
|
[
|
||||||
}),
|
"^/identity/groups$",
|
||||||
new Route(new RegExp("^/events/log$"), async () => {
|
async () => {
|
||||||
await import("@goauthentik/admin/events/EventListPage");
|
await import("@goauthentik/admin/groups/GroupListPage");
|
||||||
return html`<ak-event-list></ak-event-list>`;
|
return html`<ak-group-list></ak-group-list>`;
|
||||||
}),
|
},
|
||||||
new Route(new RegExp(`^/events/log/(?<id>${UUID_REGEX})$`), async (args) => {
|
],
|
||||||
await import("@goauthentik/admin/events/EventViewPage");
|
[
|
||||||
return html`<ak-event-view .eventID=${args.id}></ak-event-view>`;
|
`^/identity/groups/(?<uuid>${UUID_REGEX})$`,
|
||||||
}),
|
async (args) => {
|
||||||
new Route(new RegExp("^/events/transports$"), async () => {
|
await import("@goauthentik/admin/groups/GroupViewPage");
|
||||||
await import("@goauthentik/admin/events/TransportListPage");
|
return html`<ak-group-view .groupId=${args.uuid}></ak-group-view>`;
|
||||||
return html`<ak-event-transport-list></ak-event-transport-list>`;
|
},
|
||||||
}),
|
],
|
||||||
new Route(new RegExp("^/events/rules$"), async () => {
|
[
|
||||||
await import("@goauthentik/admin/events/RuleListPage");
|
"^/identity/users$",
|
||||||
return html`<ak-event-rule-list></ak-event-rule-list>`;
|
async () => {
|
||||||
}),
|
await import("@goauthentik/admin/users/UserListPage");
|
||||||
new Route(new RegExp("^/outpost/outposts$"), async () => {
|
return html`<ak-user-list></ak-user-list>`;
|
||||||
await import("@goauthentik/admin/outposts/OutpostListPage");
|
},
|
||||||
return html`<ak-outpost-list></ak-outpost-list>`;
|
],
|
||||||
}),
|
[
|
||||||
new Route(new RegExp("^/outpost/integrations$"), async () => {
|
`^/identity/users/(?<id>${ID_REGEX})$`,
|
||||||
await import("@goauthentik/admin/outposts/ServiceConnectionListPage");
|
async (args) => {
|
||||||
return html`<ak-outpost-service-connection-list></ak-outpost-service-connection-list>`;
|
await import("@goauthentik/admin/users/UserViewPage");
|
||||||
}),
|
return html`<ak-user-view .userId=${parseInt(args.id, 10)}></ak-user-view>`;
|
||||||
new Route(new RegExp("^/crypto/certificates$"), async () => {
|
},
|
||||||
await import("@goauthentik/admin/crypto/CertificateKeyPairListPage");
|
],
|
||||||
return html`<ak-crypto-certificate-list></ak-crypto-certificate-list>`;
|
[
|
||||||
}),
|
"^/identity/roles$",
|
||||||
new Route(new RegExp("^/admin/settings$"), async () => {
|
async () => {
|
||||||
await import("@goauthentik/admin/admin-settings/AdminSettingsPage");
|
await import("@goauthentik/admin/roles/RoleListPage");
|
||||||
return html`<ak-admin-settings></ak-admin-settings>`;
|
return html`<ak-role-list></ak-role-list>`;
|
||||||
}),
|
},
|
||||||
new Route(new RegExp("^/blueprints/instances$"), async () => {
|
],
|
||||||
await import("@goauthentik/admin/blueprints/BlueprintListPage");
|
[
|
||||||
return html`<ak-blueprint-list></ak-blueprint-list>`;
|
`^/identity/roles/(?<id>${UUID_REGEX})$`,
|
||||||
}),
|
async (args) => {
|
||||||
new Route(new RegExp("^/debug$"), async () => {
|
await import("@goauthentik/admin/roles/RoleViewPage");
|
||||||
await import("@goauthentik/admin/DebugPage");
|
return html`<ak-role-view roleId=${args.id}></ak-role-view>`;
|
||||||
return html`<ak-admin-debug-page></ak-admin-debug-page>`;
|
},
|
||||||
}),
|
],
|
||||||
new Route(new RegExp("^/enterprise/licenses$"), async () => {
|
[
|
||||||
await import("@goauthentik/admin/enterprise/EnterpriseLicenseListPage");
|
"^/flow/stages/invitations$",
|
||||||
return html`<ak-enterprise-license-list></ak-enterprise-license-list>`;
|
async () => {
|
||||||
}),
|
await import("@goauthentik/admin/stages/invitation/InvitationListPage");
|
||||||
|
return html`<ak-stage-invitation-list></ak-stage-invitation-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/flow/stages/prompts$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/stages/prompt/PromptListPage");
|
||||||
|
return html`<ak-stage-prompt-list></ak-stage-prompt-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/flow/stages$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/stages/StageListPage");
|
||||||
|
return html`<ak-stage-list></ak-stage-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/flow/flows$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/flows/FlowListPage");
|
||||||
|
return html`<ak-flow-list></ak-flow-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`^/flow/flows/(?<slug>${SLUG_REGEX})$`,
|
||||||
|
async (args) => {
|
||||||
|
await import("@goauthentik/admin/flows/FlowViewPage");
|
||||||
|
return html`<ak-flow-view .flowSlug=${args.slug}></ak-flow-view>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/events/log$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/events/EventListPage");
|
||||||
|
return html`<ak-event-list></ak-event-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`^/events/log/(?<id>${UUID_REGEX})$`,
|
||||||
|
async (args) => {
|
||||||
|
await import("@goauthentik/admin/events/EventViewPage");
|
||||||
|
return html`<ak-event-view .eventID=${args.id}></ak-event-view>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/events/transports$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/events/TransportListPage");
|
||||||
|
return html`<ak-event-transport-list></ak-event-transport-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/events/rules$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/events/RuleListPage");
|
||||||
|
return html`<ak-event-rule-list></ak-event-rule-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/outpost/outposts$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/outposts/OutpostListPage");
|
||||||
|
return html`<ak-outpost-list></ak-outpost-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/outpost/integrations$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/outposts/ServiceConnectionListPage");
|
||||||
|
return html`<ak-outpost-service-connection-list></ak-outpost-service-connection-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/crypto/certificates$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/crypto/CertificateKeyPairListPage");
|
||||||
|
return html`<ak-crypto-certificate-list></ak-crypto-certificate-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/admin/settings$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/admin-settings/AdminSettingsPage");
|
||||||
|
return html`<ak-admin-settings></ak-admin-settings>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/blueprints/instances$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/blueprints/BlueprintListPage");
|
||||||
|
return html`<ak-blueprint-list></ak-blueprint-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/debug$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/DebugPage");
|
||||||
|
return html`<ak-admin-debug-page></ak-admin-debug-page>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"^/enterprise/licenses$",
|
||||||
|
async () => {
|
||||||
|
await import("@goauthentik/admin/enterprise/EnterpriseLicenseListPage");
|
||||||
|
return html`<ak-enterprise-license-list></ak-enterprise-license-list>`;
|
||||||
|
},
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const ROUTES = _ROUTES.map(makeRoute);
|
||||||
|
|||||||
@ -31,15 +31,9 @@ export class VersionStatusCard extends AdminStatusCard<Version> {
|
|||||||
message: html`${msg(str`${value.versionLatest} is available!`)}`,
|
message: html`${msg(str`${value.versionLatest} is available!`)}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (value.versionLatestValid) {
|
|
||||||
return Promise.resolve<AdminStatus>({
|
|
||||||
icon: "fa fa-check-circle pf-m-success",
|
|
||||||
message: html`${msg("Up-to-date!")}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Promise.resolve<AdminStatus>({
|
return Promise.resolve<AdminStatus>({
|
||||||
icon: "fa fa-question-circle",
|
icon: "fa fa-check-circle pf-m-success",
|
||||||
message: html`${msg("Latest version unknown")}`,
|
message: html`${msg("Up-to-date!")}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import "@goauthentik/components/ak-status-label";
|
import "@goauthentik/components/ak-status-label";
|
||||||
import "@goauthentik/elements/events/LogViewer";
|
|
||||||
import { Form } from "@goauthentik/elements/forms/Form";
|
import { Form } from "@goauthentik/elements/forms/Form";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
import "@goauthentik/elements/forms/SearchSelect";
|
import "@goauthentik/elements/forms/SearchSelect";
|
||||||
@ -84,7 +83,28 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
|||||||
<div class="pf-c-form__group-label">
|
<div class="pf-c-form__group-label">
|
||||||
<div class="c-form__horizontal-group">
|
<div class="c-form__horizontal-group">
|
||||||
<dl class="pf-c-description-list pf-m-horizontal">
|
<dl class="pf-c-description-list pf-m-horizontal">
|
||||||
<ak-log-viewer .logs=${this.result?.logMessages}></ak-log-viewer>
|
${(this.result?.logMessages || []).length > 0
|
||||||
|
? this.result?.logMessages?.map((m) => {
|
||||||
|
return html`<div class="pf-c-description-list__group">
|
||||||
|
<dt class="pf-c-description-list__term">
|
||||||
|
<span class="pf-c-description-list__text"
|
||||||
|
>${m.log_level}</span
|
||||||
|
>
|
||||||
|
</dt>
|
||||||
|
<dd class="pf-c-description-list__description">
|
||||||
|
<div class="pf-c-description-list__text">
|
||||||
|
${m.event}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>`;
|
||||||
|
})
|
||||||
|
: html`<div class="pf-c-description-list__group">
|
||||||
|
<dt class="pf-c-description-list__term">
|
||||||
|
<span class="pf-c-description-list__text"
|
||||||
|
>${msg("No log messages.")}</span
|
||||||
|
>
|
||||||
|
</dt>
|
||||||
|
</div>`}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -150,6 +150,10 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
|||||||
|
|
||||||
renderSectionBefore(): TemplateResult {
|
renderSectionBefore(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
|
<div class="pf-c-banner pf-m-info">
|
||||||
|
${msg("Enterprise is in preview.")}
|
||||||
|
<a href="mailto:hello@goauthentik.io">${msg("Send us feedback!")}</a>
|
||||||
|
</div>
|
||||||
<section class="pf-c-page__main-section pf-m-no-padding-bottom">
|
<section class="pf-c-page__main-section pf-m-no-padding-bottom">
|
||||||
<div
|
<div
|
||||||
class="pf-l-grid pf-m-gutter pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-3-col-on-lg pf-m-all-3-col-on-xl"
|
class="pf-l-grid pf-m-gutter pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-3-col-on-lg pf-m-all-3-col-on-xl"
|
||||||
|
|||||||
@ -23,15 +23,13 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
|||||||
checkbox = true;
|
checkbox = true;
|
||||||
clearOnRefresh = true;
|
clearOnRefresh = true;
|
||||||
|
|
||||||
order = "order";
|
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
target?: string;
|
target?: string;
|
||||||
|
|
||||||
async apiEndpoint(page: number): Promise<PaginatedResponse<FlowStageBinding>> {
|
async apiEndpoint(page: number): Promise<PaginatedResponse<FlowStageBinding>> {
|
||||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
|
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
|
||||||
target: this.target || "",
|
target: this.target || "",
|
||||||
ordering: this.order,
|
ordering: "order",
|
||||||
page: page,
|
page: page,
|
||||||
pageSize: (await uiConfig()).pagination.perPage,
|
pageSize: (await uiConfig()).pagination.perPage,
|
||||||
});
|
});
|
||||||
@ -39,8 +37,8 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
|||||||
|
|
||||||
columns(): TableColumn[] {
|
columns(): TableColumn[] {
|
||||||
return [
|
return [
|
||||||
new TableColumn(msg("Order"), "order"),
|
new TableColumn(msg("Order")),
|
||||||
new TableColumn(msg("Name"), "stage__name"),
|
new TableColumn(msg("Name")),
|
||||||
new TableColumn(msg("Type")),
|
new TableColumn(msg("Type")),
|
||||||
new TableColumn(msg("Actions")),
|
new TableColumn(msg("Actions")),
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||||
import "@goauthentik/components/ak-status-label";
|
import "@goauthentik/components/ak-status-label";
|
||||||
import "@goauthentik/elements/events/LogViewer";
|
|
||||||
import { Form } from "@goauthentik/elements/forms/Form";
|
import { Form } from "@goauthentik/elements/forms/Form";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
|
|
||||||
@ -56,7 +55,28 @@ export class FlowImportForm extends Form<Flow> {
|
|||||||
<div class="pf-c-form__group-label">
|
<div class="pf-c-form__group-label">
|
||||||
<div class="c-form__horizontal-group">
|
<div class="c-form__horizontal-group">
|
||||||
<dl class="pf-c-description-list pf-m-horizontal">
|
<dl class="pf-c-description-list pf-m-horizontal">
|
||||||
<ak-log-viewer .logs=${this.result?.logs}></ak-log-viewer>
|
${(this.result?.logs || []).length > 0
|
||||||
|
? this.result?.logs?.map((m) => {
|
||||||
|
return html`<div class="pf-c-description-list__group">
|
||||||
|
<dt class="pf-c-description-list__term">
|
||||||
|
<span class="pf-c-description-list__text"
|
||||||
|
>${m.log_level}</span
|
||||||
|
>
|
||||||
|
</dt>
|
||||||
|
<dd class="pf-c-description-list__description">
|
||||||
|
<div class="pf-c-description-list__text">
|
||||||
|
${m.event}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>`;
|
||||||
|
})
|
||||||
|
: html`<div class="pf-c-description-list__group">
|
||||||
|
<dt class="pf-c-description-list__term">
|
||||||
|
<span class="pf-c-description-list__text"
|
||||||
|
>${msg("No log messages.")}</span
|
||||||
|
>
|
||||||
|
</dt>
|
||||||
|
</div>`}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -31,12 +31,10 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
|||||||
checkbox = true;
|
checkbox = true;
|
||||||
clearOnRefresh = true;
|
clearOnRefresh = true;
|
||||||
|
|
||||||
order = "order";
|
|
||||||
|
|
||||||
async apiEndpoint(page: number): Promise<PaginatedResponse<PolicyBinding>> {
|
async apiEndpoint(page: number): Promise<PaginatedResponse<PolicyBinding>> {
|
||||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({
|
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsList({
|
||||||
target: this.target || "",
|
target: this.target || "",
|
||||||
ordering: this.order,
|
ordering: "order",
|
||||||
page: page,
|
page: page,
|
||||||
pageSize: (await uiConfig()).pagination.perPage,
|
pageSize: (await uiConfig()).pagination.perPage,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { first } from "@goauthentik/common/utils";
|
|||||||
import "@goauthentik/components/ak-status-label";
|
import "@goauthentik/components/ak-status-label";
|
||||||
import "@goauthentik/elements/CodeMirror";
|
import "@goauthentik/elements/CodeMirror";
|
||||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||||
import "@goauthentik/elements/events/LogViewer";
|
|
||||||
import { Form } from "@goauthentik/elements/forms/Form";
|
import { Form } from "@goauthentik/elements/forms/Form";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
import "@goauthentik/elements/forms/SearchSelect";
|
import "@goauthentik/elements/forms/SearchSelect";
|
||||||
@ -86,7 +85,28 @@ export class PolicyTestForm extends Form<PolicyTestRequest> {
|
|||||||
<div class="pf-c-form__group-label">
|
<div class="pf-c-form__group-label">
|
||||||
<div class="c-form__horizontal-group">
|
<div class="c-form__horizontal-group">
|
||||||
<dl class="pf-c-description-list pf-m-horizontal">
|
<dl class="pf-c-description-list pf-m-horizontal">
|
||||||
<ak-log-viewer .logs=${this.result?.logMessages}></ak-log-viewer>
|
${(this.result?.logMessages || []).length > 0
|
||||||
|
? this.result?.logMessages?.map((m) => {
|
||||||
|
return html`<div class="pf-c-description-list__group">
|
||||||
|
<dt class="pf-c-description-list__term">
|
||||||
|
<span class="pf-c-description-list__text"
|
||||||
|
>${m.log_level}</span
|
||||||
|
>
|
||||||
|
</dt>
|
||||||
|
<dd class="pf-c-description-list__description">
|
||||||
|
<div class="pf-c-description-list__text">
|
||||||
|
${m.event}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>`;
|
||||||
|
})
|
||||||
|
: html`<div class="pf-c-description-list__group">
|
||||||
|
<dt class="pf-c-description-list__term">
|
||||||
|
<span class="pf-c-description-list__text"
|
||||||
|
>${msg("No log messages.")}</span
|
||||||
|
>
|
||||||
|
</dt>
|
||||||
|
</div>`}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -175,7 +175,7 @@ export class OAuth2ProviderViewPage extends AKElement {
|
|||||||
</div>`}
|
</div>`}
|
||||||
<div class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
|
<div class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter">
|
||||||
<div
|
<div
|
||||||
class="pf-c-card pf-l-grid__item pf-l-grid__item pf-m-12-col pf-m-4-col-on-xl pf-m-4-col-on-2xl"
|
class="pf-c-card pf-l-grid__item pf-l-grid__item pf-m-12-col pf-m-3-col-on-xl pf-m-3-col-on-2xl"
|
||||||
>
|
>
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<dl class="pf-c-description-list">
|
<dl class="pf-c-description-list">
|
||||||
@ -255,7 +255,7 @@ export class OAuth2ProviderViewPage extends AKElement {
|
|||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-card pf-l-grid__item pf-m-8-col">
|
<div class="pf-c-card pf-l-grid__item pf-m-7-col">
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<form class="pf-c-form">
|
<form class="pf-c-form">
|
||||||
<div class="pf-c-form__group">
|
<div class="pf-c-form__group">
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { customElement, property } from "lit/decorators.js";
|
|||||||
|
|
||||||
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||||
|
|
||||||
import { ConnectionToken, RACProvider, RacApi } from "@goauthentik/api";
|
import { ConnectionToken, Endpoint, RACProvider, RacApi } from "@goauthentik/api";
|
||||||
|
|
||||||
@customElement("ak-rac-connection-token-list")
|
@customElement("ak-rac-connection-token-list")
|
||||||
export class ConnectionTokenListPage extends Table<ConnectionToken> {
|
export class ConnectionTokenListPage extends Table<ConnectionToken> {
|
||||||
@ -53,20 +53,20 @@ export class ConnectionTokenListPage extends Table<ConnectionToken> {
|
|||||||
return html`<ak-forms-delete-bulk
|
return html`<ak-forms-delete-bulk
|
||||||
objectLabel=${msg("Connection Token(s)")}
|
objectLabel=${msg("Connection Token(s)")}
|
||||||
.objects=${this.selectedElements}
|
.objects=${this.selectedElements}
|
||||||
.metadata=${(item: ConnectionToken) => {
|
.metadata=${(item: Endpoint) => {
|
||||||
return [
|
return [
|
||||||
{ key: msg("Endpoint"), value: item.endpointObj.name },
|
{ key: msg("Name"), value: item.name },
|
||||||
{ key: msg("User"), value: item.user.username },
|
{ key: msg("Host"), value: item.host },
|
||||||
];
|
];
|
||||||
}}
|
}}
|
||||||
.usedBy=${(item: ConnectionToken) => {
|
.usedBy=${(item: Endpoint) => {
|
||||||
return new RacApi(DEFAULT_CONFIG).racConnectionTokensUsedByList({
|
return new RacApi(DEFAULT_CONFIG).racConnectionTokensUsedByList({
|
||||||
connectionTokenUuid: item.pk || "",
|
connectionTokenUuid: item.pk,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
.delete=${(item: ConnectionToken) => {
|
.delete=${(item: Endpoint) => {
|
||||||
return new RacApi(DEFAULT_CONFIG).racConnectionTokensDestroy({
|
return new RacApi(DEFAULT_CONFIG).racConnectionTokensDestroy({
|
||||||
connectionTokenUuid: item.pk || "",
|
connectionTokenUuid: item.pk,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -88,11 +88,7 @@ export class RACProviderViewPage extends AKElement {
|
|||||||
<section slot="page-overview" data-tab-title="${msg("Overview")}">
|
<section slot="page-overview" data-tab-title="${msg("Overview")}">
|
||||||
${this.renderTabOverview()}
|
${this.renderTabOverview()}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section slot="page-connections" data-tab-title="${msg("Connections")}">
|
||||||
slot="page-connections"
|
|
||||||
data-tab-title="${msg("Connections")}"
|
|
||||||
class="pf-c-page__main-section pf-m-no-padding-mobile"
|
|
||||||
>
|
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-rac-connection-token-list
|
<ak-rac-connection-token-list
|
||||||
|
|||||||
@ -86,28 +86,6 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
|||||||
<span class="pf-c-switch__label">${msg("Enabled")}</span>
|
<span class="pf-c-switch__label">${msg("Enabled")}</span>
|
||||||
</label>
|
</label>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal name="passwordLoginUpdateInternalPassword">
|
|
||||||
<label class="pf-c-switch">
|
|
||||||
<input
|
|
||||||
class="pf-c-switch__input"
|
|
||||||
type="checkbox"
|
|
||||||
?checked=${first(this.instance?.passwordLoginUpdateInternalPassword, false)}
|
|
||||||
/>
|
|
||||||
<span class="pf-c-switch__toggle">
|
|
||||||
<span class="pf-c-switch__toggle-icon">
|
|
||||||
<i class="fas fa-check" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span class="pf-c-switch__label"
|
|
||||||
>${msg("Update internal password on login")}</span
|
|
||||||
>
|
|
||||||
</label>
|
|
||||||
<p class="pf-c-form__helper-text">
|
|
||||||
${msg(
|
|
||||||
"When the user logs in to authentik using this source password backend, update their credentials in authentik.",
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</ak-form-element-horizontal>
|
|
||||||
<ak-form-element-horizontal name="syncUsers">
|
<ak-form-element-horizontal name="syncUsers">
|
||||||
<label class="pf-c-switch">
|
<label class="pf-c-switch">
|
||||||
<input
|
<input
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import "@goauthentik/admin/stages/identification/IdentificationStageForm";
|
|||||||
import "@goauthentik/admin/stages/invitation/InvitationStageForm";
|
import "@goauthentik/admin/stages/invitation/InvitationStageForm";
|
||||||
import "@goauthentik/admin/stages/password/PasswordStageForm";
|
import "@goauthentik/admin/stages/password/PasswordStageForm";
|
||||||
import "@goauthentik/admin/stages/prompt/PromptStageForm";
|
import "@goauthentik/admin/stages/prompt/PromptStageForm";
|
||||||
import "@goauthentik/admin/stages/source/SourceStageForm";
|
|
||||||
import "@goauthentik/admin/stages/user_delete/UserDeleteStageForm";
|
import "@goauthentik/admin/stages/user_delete/UserDeleteStageForm";
|
||||||
import "@goauthentik/admin/stages/user_login/UserLoginStageForm";
|
import "@goauthentik/admin/stages/user_login/UserLoginStageForm";
|
||||||
import "@goauthentik/admin/stages/user_logout/UserLogoutStageForm";
|
import "@goauthentik/admin/stages/user_logout/UserLogoutStageForm";
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import "@goauthentik/admin/common/ak-license-notice";
|
|
||||||
import { StageBindingForm } from "@goauthentik/admin/flows/StageBindingForm";
|
import { StageBindingForm } from "@goauthentik/admin/flows/StageBindingForm";
|
||||||
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
|
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
|
||||||
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
|
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
|
||||||
@ -15,14 +14,12 @@ import "@goauthentik/admin/stages/identification/IdentificationStageForm";
|
|||||||
import "@goauthentik/admin/stages/invitation/InvitationStageForm";
|
import "@goauthentik/admin/stages/invitation/InvitationStageForm";
|
||||||
import "@goauthentik/admin/stages/password/PasswordStageForm";
|
import "@goauthentik/admin/stages/password/PasswordStageForm";
|
||||||
import "@goauthentik/admin/stages/prompt/PromptStageForm";
|
import "@goauthentik/admin/stages/prompt/PromptStageForm";
|
||||||
import "@goauthentik/admin/stages/source/SourceStageForm";
|
|
||||||
import "@goauthentik/admin/stages/user_delete/UserDeleteStageForm";
|
import "@goauthentik/admin/stages/user_delete/UserDeleteStageForm";
|
||||||
import "@goauthentik/admin/stages/user_login/UserLoginStageForm";
|
import "@goauthentik/admin/stages/user_login/UserLoginStageForm";
|
||||||
import "@goauthentik/admin/stages/user_logout/UserLogoutStageForm";
|
import "@goauthentik/admin/stages/user_logout/UserLogoutStageForm";
|
||||||
import "@goauthentik/admin/stages/user_write/UserWriteStageForm";
|
import "@goauthentik/admin/stages/user_write/UserWriteStageForm";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
|
||||||
import "@goauthentik/elements/forms/ProxyForm";
|
import "@goauthentik/elements/forms/ProxyForm";
|
||||||
import "@goauthentik/elements/wizard/FormWizardPage";
|
import "@goauthentik/elements/wizard/FormWizardPage";
|
||||||
import { FormWizardPage } from "@goauthentik/elements/wizard/FormWizardPage";
|
import { FormWizardPage } from "@goauthentik/elements/wizard/FormWizardPage";
|
||||||
@ -31,7 +28,7 @@ import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";
|
|||||||
|
|
||||||
import { msg, str } from "@lit/localize";
|
import { msg, str } from "@lit/localize";
|
||||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
||||||
import { CSSResult, TemplateResult, html, nothing } from "lit";
|
import { CSSResult, TemplateResult, html } from "lit";
|
||||||
import { property } from "lit/decorators.js";
|
import { property } from "lit/decorators.js";
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
@ -42,7 +39,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|||||||
import { FlowStageBinding, Stage, StagesApi, TypeCreate } from "@goauthentik/api";
|
import { FlowStageBinding, Stage, StagesApi, TypeCreate } from "@goauthentik/api";
|
||||||
|
|
||||||
@customElement("ak-stage-wizard-initial")
|
@customElement("ak-stage-wizard-initial")
|
||||||
export class InitialStageWizardPage extends WithLicenseSummary(WizardPage) {
|
export class InitialStageWizardPage extends WizardPage {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
stageTypes: TypeCreate[] = [];
|
stageTypes: TypeCreate[] = [];
|
||||||
sidebarLabel = () => msg("Select type");
|
sidebarLabel = () => msg("Select type");
|
||||||
@ -65,7 +62,6 @@ export class InitialStageWizardPage extends WithLicenseSummary(WizardPage) {
|
|||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html`<form class="pf-c-form pf-m-horizontal">
|
return html`<form class="pf-c-form pf-m-horizontal">
|
||||||
${this.stageTypes.map((type) => {
|
${this.stageTypes.map((type) => {
|
||||||
const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
|
|
||||||
return html`<div class="pf-c-radio">
|
return html`<div class="pf-c-radio">
|
||||||
<input
|
<input
|
||||||
class="pf-c-radio__input"
|
class="pf-c-radio__input"
|
||||||
@ -86,15 +82,11 @@ export class InitialStageWizardPage extends WithLicenseSummary(WizardPage) {
|
|||||||
);
|
);
|
||||||
this.host.isValid = true;
|
this.host.isValid = true;
|
||||||
}}
|
}}
|
||||||
?disabled=${requiresEnterprise}
|
|
||||||
/>
|
/>
|
||||||
<label class="pf-c-radio__label" for=${`${type.component}-${type.modelName}`}
|
<label class="pf-c-radio__label" for=${`${type.component}-${type.modelName}`}
|
||||||
>${type.name}</label
|
>${type.name}</label
|
||||||
>
|
>
|
||||||
<span class="pf-c-radio__description">${type.description}${
|
<span class="pf-c-radio__description">${type.description}</span>
|
||||||
requiresEnterprise ? html`<ak-license-notice></ak-license-notice>` : nothing
|
|
||||||
}</span>
|
|
||||||
</span>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
})}
|
})}
|
||||||
</form>`;
|
</form>`;
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
|
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
|
||||||
import "@goauthentik/elements/forms/SearchSelect/index";
|
|
||||||
import "@goauthentik/elements/utils/TimeDeltaHelp";
|
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
|
||||||
import { TemplateResult, html } from "lit";
|
|
||||||
import { customElement } from "lit/decorators.js";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Source,
|
|
||||||
SourceStage,
|
|
||||||
SourcesAllListRequest,
|
|
||||||
SourcesApi,
|
|
||||||
StagesApi,
|
|
||||||
} from "@goauthentik/api";
|
|
||||||
|
|
||||||
@customElement("ak-stage-source-form")
|
|
||||||
export class SourceStageForm extends BaseStageForm<SourceStage> {
|
|
||||||
loadInstance(pk: string): Promise<SourceStage> {
|
|
||||||
return new StagesApi(DEFAULT_CONFIG).stagesSourceRetrieve({
|
|
||||||
stageUuid: pk,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async send(data: SourceStage): Promise<SourceStage> {
|
|
||||||
if (this.instance) {
|
|
||||||
return new StagesApi(DEFAULT_CONFIG).stagesSourceUpdate({
|
|
||||||
stageUuid: this.instance.pk || "",
|
|
||||||
sourceStageRequest: data,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return new StagesApi(DEFAULT_CONFIG).stagesSourceCreate({
|
|
||||||
sourceStageRequest: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderForm(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<span> ${msg("TODO.")} </span>
|
|
||||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value="${ifDefined(this.instance?.name || "")}"
|
|
||||||
class="pf-c-form-control"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</ak-form-element-horizontal>
|
|
||||||
<ak-form-element-horizontal label=${msg("Source")} ?required=${true} name="source">
|
|
||||||
<ak-search-select
|
|
||||||
.fetchObjects=${async (query?: string): Promise<Source[]> => {
|
|
||||||
const args: SourcesAllListRequest = {
|
|
||||||
ordering: "name",
|
|
||||||
};
|
|
||||||
if (query !== undefined) {
|
|
||||||
args.search = query;
|
|
||||||
}
|
|
||||||
const users = await new SourcesApi(DEFAULT_CONFIG).sourcesAllList(args);
|
|
||||||
return users.results;
|
|
||||||
}}
|
|
||||||
.renderElement=${(source: Source): string => {
|
|
||||||
return source.name;
|
|
||||||
}}
|
|
||||||
.renderDescription=${(source: Source): TemplateResult => {
|
|
||||||
return html`${source.verboseName}`;
|
|
||||||
}}
|
|
||||||
.value=${(source: Source | undefined): string | undefined => {
|
|
||||||
return source?.pk;
|
|
||||||
}}
|
|
||||||
.selected=${(source: Source): boolean => {
|
|
||||||
return source.pk === this.instance?.source;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
</ak-search-select>
|
|
||||||
</ak-form-element-horizontal>
|
|
||||||
<ak-form-element-horizontal
|
|
||||||
label=${msg("Resume timeout")}
|
|
||||||
?required=${true}
|
|
||||||
name="resumeTimeout"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value="${ifDefined(this.instance?.resumeTimeout || "minutes=10")}"
|
|
||||||
class="pf-c-form-control"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<p class="pf-c-form__helper-text">
|
|
||||||
${msg(
|
|
||||||
"Amount of time a user can take to return from the source to continue the flow.",
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
|
||||||
</ak-form-element-horizontal>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user