Compare commits
148 Commits
web/fix-cs
...
providers/
Author | SHA1 | Date | |
---|---|---|---|
739acf50f4 | |||
ac1f3332dc | |||
2c64f72ebc | |||
51a8670a13 | |||
b9f6cd9226 | |||
7010682122 | |||
0e82facfb4 | |||
afdff95453 | |||
b11f12b1db | |||
4df906e32c | |||
fee7abed7c | |||
d1a5d0dd7e | |||
d1e06b1c7e | |||
458b2b5c55 | |||
c0b1cd7674 | |||
8305a52ae2 | |||
b77cdfe96b | |||
0dcb261b4c | |||
46bddbf067 | |||
b8b6c0cd98 | |||
64fbbcf3e8 | |||
a4c6b76686 | |||
c8c7f77813 | |||
dde4314127 | |||
0b620f54f3 | |||
dc10ab0e66 | |||
8d92e3d78d | |||
ae66df6d9a | |||
ed3108fbd4 | |||
f2199f1712 | |||
e5810b31c5 | |||
d8b6a06522 | |||
c8ab6c728d | |||
e854623967 | |||
0b4822c1e3 | |||
fcb82c243f | |||
4415bee62a | |||
42b00110e7 | |||
0cce67dd15 | |||
f7a300fb30 | |||
ca260b700f | |||
8e9fbff5bd | |||
f2a8b82249 | |||
11a3cf84fa | |||
d506e5d50c | |||
7f8b8a7eb5 | |||
06af8e3a35 | |||
bf8c3078db | |||
15c7be1979 | |||
285dc8cff0 | |||
d7e399dbf9 | |||
1e25d3e3e9 | |||
d5c0a6e252 | |||
8a5aa9bf6f | |||
6584074b9c | |||
1d773dfc76 | |||
193b9e1ae8 | |||
32f95818db | |||
bcb7c72907 | |||
51a33f330c | |||
da2eddfb5a | |||
75e9a02bd2 | |||
af239027d5 | |||
6ce83e5271 | |||
c804a7e77d | |||
9d9acab603 | |||
8e42eb0546 | |||
f39c0e6bd9 | |||
81ac09695a | |||
26c5653182 | |||
0f7a3875f7 | |||
0036ecf956 | |||
96554de17a | |||
fabd1e39ae | |||
f992bfa8ff | |||
f1a04674fb | |||
ec4c31e37d | |||
ac520cd872 | |||
50e493d692 | |||
d49d8bc559 | |||
3e94b58afb | |||
1b81973358 | |||
880ca9a57d | |||
4d8d12f917 | |||
e78e4165da | |||
e4c7c24ae4 | |||
3b8daf7cc9 | |||
104e70c383 | |||
82ac7d195d | |||
d19d075326 | |||
ae03e4679e | |||
05b0e2c164 | |||
ff5680fb0e | |||
88cf0efb81 | |||
7783b200a3 | |||
d13954970e | |||
743a781eba | |||
f53f3c77be | |||
61b61ce960 | |||
09e6b80fd6 | |||
4cad5f7b40 | |||
3f43ff22a8 | |||
cf6bbbae70 | |||
ac1ef5139c | |||
ce0775239d | |||
56f267146f | |||
d98af5a0b1 | |||
3b3c874175 | |||
1f19e5cb3e | |||
f2062e75a1 | |||
ff5df458af | |||
6a8c5ca650 | |||
01a3516478 | |||
868ce06f67 | |||
e5b6dc5508 | |||
ee86322ab4 | |||
52d19bf4a6 | |||
fdcc1dcb36 | |||
5805ac83f7 | |||
772048092b | |||
be1219a73f | |||
9ab057fafc | |||
f9b6c8cef9 | |||
f159973d8b | |||
4a2f97710e | |||
735a8e77e2 | |||
e50cc20f76 | |||
5c19c6ea7f | |||
4c0b6c71ac | |||
cfc065b41b | |||
d81381bda6 | |||
6613553c13 | |||
9a304cc198 | |||
ebaec17703 | |||
6fcc06bfe0 | |||
2ba66f4f91 | |||
f9fc32e89c | |||
ee275d36bf | |||
ed39123f4e | |||
68726b0921 | |||
74a91aafe8 | |||
a15853ed55 | |||
7c51657aa1 | |||
86e9639d0c | |||
1620131ed5 | |||
743ee53bd9 | |||
7a04d97bdf | |||
6c99194f42 |
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
@ -7,8 +7,6 @@ on:
|
||||
- main
|
||||
- next
|
||||
- version-*
|
||||
paths-ignore:
|
||||
- website/**
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
@ -10,7 +10,7 @@ from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from authentik import __version__, get_build_hash
|
||||
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
|
||||
from authentik.admin.tasks import VERSION_CACHE_KEY, VERSION_NULL, update_latest_version
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ class VersionSerializer(PassiveSerializer):
|
||||
|
||||
version_current = SerializerMethodField()
|
||||
version_latest = SerializerMethodField()
|
||||
version_latest_valid = SerializerMethodField()
|
||||
build_hash = SerializerMethodField()
|
||||
outdated = SerializerMethodField()
|
||||
|
||||
@ -38,6 +39,10 @@ class VersionSerializer(PassiveSerializer):
|
||||
return __version__
|
||||
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:
|
||||
"""Check if we're running the latest version"""
|
||||
return parse(self.get_version_current(instance)) < parse(self.get_version_latest(instance))
|
||||
|
@ -18,6 +18,7 @@ from authentik.lib.utils.http import get_http_session
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
LOGGER = get_logger()
|
||||
VERSION_NULL = "0.0.0"
|
||||
VERSION_CACHE_KEY = "authentik_latest_version"
|
||||
VERSION_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours
|
||||
# Chop of the first ^ because we want to search the entire string
|
||||
@ -55,7 +56,7 @@ def clear_update_notifications():
|
||||
def update_latest_version(self: SystemTask):
|
||||
"""Update latest version info"""
|
||||
if CONFIG.get_bool("disable_update_check"):
|
||||
cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
|
||||
cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
|
||||
self.set_status(TaskStatus.WARNING, "Version check disabled.")
|
||||
return
|
||||
try:
|
||||
@ -82,7 +83,7 @@ def update_latest_version(self: SystemTask):
|
||||
event_dict["message"] = f"Changelog: {match.group()}"
|
||||
Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
|
||||
except (RequestException, IndexError) as exc:
|
||||
cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
|
||||
cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
|
||||
self.set_error(exc)
|
||||
|
||||
|
||||
|
@ -68,7 +68,11 @@ class ConfigView(APIView):
|
||||
"""Get all capabilities this server instance supports"""
|
||||
caps = []
|
||||
deb_test = settings.DEBUG or settings.TEST
|
||||
if Path(settings.MEDIA_ROOT).is_mount() or deb_test:
|
||||
if (
|
||||
CONFIG.get("storage.media.backend", "file") == "s3"
|
||||
or Path(settings.STORAGES["default"]["OPTIONS"]["location"]).is_mount()
|
||||
or deb_test
|
||||
):
|
||||
caps.append(Capabilities.CAN_SAVE_MEDIA)
|
||||
for processor in get_context_processors():
|
||||
if cap := processor.capability():
|
||||
|
@ -19,8 +19,6 @@ from guardian.models import UserObjectPermission
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.serializers import BaseSerializer, Serializer
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
from structlog.testing import capture_logs
|
||||
from structlog.types import EventDict
|
||||
from yaml import load
|
||||
|
||||
from authentik.blueprints.v1.common import (
|
||||
@ -42,6 +40,7 @@ from authentik.core.models import (
|
||||
from authentik.enterprise.license import LicenseKey
|
||||
from authentik.enterprise.models import LicenseUsage
|
||||
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.utils import cleanse_dict
|
||||
from authentik.flows.models import FlowToken, Stage
|
||||
@ -161,7 +160,7 @@ class Importer:
|
||||
|
||||
def updater(value) -> Any:
|
||||
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 value
|
||||
|
||||
@ -250,7 +249,7 @@ class Importer:
|
||||
model_instance = existing_models.first()
|
||||
if not isinstance(model(), BaseMetaModel) and model_instance:
|
||||
self.logger.debug(
|
||||
"initialise serializer with instance",
|
||||
"Initialise serializer with instance",
|
||||
model=model,
|
||||
instance=model_instance,
|
||||
pk=model_instance.pk,
|
||||
@ -260,14 +259,14 @@ class Importer:
|
||||
elif model_instance and entry.state == BlueprintEntryDesiredState.MUST_CREATED:
|
||||
raise EntryInvalidError.from_entry(
|
||||
(
|
||||
f"state is set to {BlueprintEntryDesiredState.MUST_CREATED} "
|
||||
f"State is set to {BlueprintEntryDesiredState.MUST_CREATED} "
|
||||
"and object exists already",
|
||||
),
|
||||
entry,
|
||||
)
|
||||
else:
|
||||
self.logger.debug(
|
||||
"initialised new serializer instance",
|
||||
"Initialised new serializer instance",
|
||||
model=model,
|
||||
**cleanse_dict(updated_identifiers),
|
||||
)
|
||||
@ -324,7 +323,7 @@ class Importer:
|
||||
model: type[SerializerModel] = registry.get_model(model_app_label, model_name)
|
||||
except LookupError:
|
||||
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
|
||||
# Validate each single entry
|
||||
@ -336,7 +335,7 @@ class Importer:
|
||||
if entry.get_state(self._import) == BlueprintEntryDesiredState.ABSENT:
|
||||
serializer = exc.serializer
|
||||
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:
|
||||
raise exc
|
||||
return False
|
||||
@ -356,14 +355,14 @@ class Importer:
|
||||
and state == BlueprintEntryDesiredState.CREATED
|
||||
):
|
||||
self.logger.debug(
|
||||
"instance exists, skipping",
|
||||
"Instance exists, skipping",
|
||||
model=model,
|
||||
instance=instance,
|
||||
pk=instance.pk,
|
||||
)
|
||||
else:
|
||||
instance = serializer.save()
|
||||
self.logger.debug("updated model", model=instance)
|
||||
self.logger.debug("Updated model", model=instance)
|
||||
if "pk" in entry.identifiers:
|
||||
self.__pk_map[entry.identifiers["pk"]] = instance.pk
|
||||
entry._state = BlueprintEntryState(instance)
|
||||
@ -371,12 +370,12 @@ class Importer:
|
||||
instance: Model | None = serializer.instance
|
||||
if instance.pk:
|
||||
instance.delete()
|
||||
self.logger.debug("deleted model", mode=instance)
|
||||
self.logger.debug("Deleted model", mode=instance)
|
||||
continue
|
||||
self.logger.debug("entry to delete with no instance, skipping")
|
||||
self.logger.debug("Entry to delete with no instance, skipping")
|
||||
return True
|
||||
|
||||
def validate(self, raise_validation_errors=False) -> tuple[bool, list[EventDict]]:
|
||||
def validate(self, raise_validation_errors=False) -> tuple[bool, list[LogEvent]]:
|
||||
"""Validate loaded blueprint export, ensure all models are allowed
|
||||
and serializers have no errors"""
|
||||
self.logger.debug("Starting blueprint import validation")
|
||||
@ -390,9 +389,7 @@ class Importer:
|
||||
):
|
||||
successful = self._apply_models(raise_errors=raise_validation_errors)
|
||||
if not successful:
|
||||
self.logger.debug("Blueprint validation failed")
|
||||
for log in logs:
|
||||
getattr(self.logger, log.get("log_level"))(**log)
|
||||
self.logger.warning("Blueprint validation failed")
|
||||
self.logger.debug("Finished blueprint import validation")
|
||||
self._import = orig_import
|
||||
return successful, logs
|
||||
|
@ -30,6 +30,7 @@ from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, E
|
||||
from authentik.blueprints.v1.importer import Importer
|
||||
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
|
||||
from authentik.blueprints.v1.oci import OCI_PREFIX
|
||||
from authentik.events.logs import capture_logs
|
||||
from authentik.events.models import TaskStatus
|
||||
from authentik.events.system_tasks import SystemTask, prefill_task
|
||||
from authentik.events.utils import sanitize_dict
|
||||
@ -211,14 +212,15 @@ def apply_blueprint(self: SystemTask, instance_pk: str):
|
||||
if not valid:
|
||||
instance.status = BlueprintInstanceStatus.ERROR
|
||||
instance.save()
|
||||
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")
|
||||
self.set_status(TaskStatus.ERROR, *logs)
|
||||
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.last_applied_hash = file_hash
|
||||
instance.last_applied = now()
|
||||
|
@ -20,15 +20,14 @@ from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from structlog.stdlib import get_logger
|
||||
from structlog.testing import capture_logs
|
||||
|
||||
from authentik.admin.api.metrics import CoordinateSerializer
|
||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||
from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.models import Application, User
|
||||
from authentik.events.logs import LogEventSerializer, capture_logs
|
||||
from authentik.events.models import EventAction
|
||||
from authentik.events.utils import sanitize_dict
|
||||
from authentik.lib.utils.file import (
|
||||
FilePathSerializer,
|
||||
FileUploadSerializer,
|
||||
@ -37,7 +36,7 @@ from authentik.lib.utils.file import (
|
||||
)
|
||||
from authentik.policies.api.exec import PolicyTestResultSerializer
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
from authentik.policies.types import PolicyResult
|
||||
from authentik.policies.types import CACHE_PREFIX, PolicyResult
|
||||
from authentik.rbac.decorators import permission_required
|
||||
from authentik.rbac.filters import ObjectFilter
|
||||
|
||||
@ -46,7 +45,7 @@ LOGGER = get_logger()
|
||||
|
||||
def user_app_cache_key(user_pk: str) -> str:
|
||||
"""Cache key where application list for user is saved"""
|
||||
return f"goauthentik.io/core/app_access/{user_pk}"
|
||||
return f"{CACHE_PREFIX}/app_access/{user_pk}"
|
||||
|
||||
|
||||
class ApplicationSerializer(ModelSerializer):
|
||||
@ -182,9 +181,9 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
if request.user.is_superuser:
|
||||
log_messages = []
|
||||
for log in logs:
|
||||
if log.get("process", "") == "PolicyProcess":
|
||||
if log.attributes.get("process", "") == "PolicyProcess":
|
||||
continue
|
||||
log_messages.append(sanitize_dict(log))
|
||||
log_messages.append(LogEventSerializer(log).data)
|
||||
result.log_messages = log_messages
|
||||
response = PolicyTestResultSerializer(result)
|
||||
return Response(response.data)
|
||||
@ -214,7 +213,7 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
return super().list(request)
|
||||
|
||||
queryset = self._filter_queryset_for_list(self.get_queryset())
|
||||
pagined_apps = self.paginate_queryset(queryset)
|
||||
paginated_apps = self.paginate_queryset(queryset)
|
||||
|
||||
if "for_user" in request.query_params:
|
||||
try:
|
||||
@ -228,18 +227,18 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
raise ValidationError({"for_user": "User not found"})
|
||||
except ValueError as exc:
|
||||
raise ValidationError from exc
|
||||
allowed_applications = self._get_allowed_applications(pagined_apps, user=for_user)
|
||||
allowed_applications = self._get_allowed_applications(paginated_apps, user=for_user)
|
||||
serializer = self.get_serializer(allowed_applications, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
allowed_applications = []
|
||||
if not should_cache:
|
||||
allowed_applications = self._get_allowed_applications(pagined_apps)
|
||||
allowed_applications = self._get_allowed_applications(paginated_apps)
|
||||
if should_cache:
|
||||
allowed_applications = cache.get(user_app_cache_key(self.request.user.pk))
|
||||
if not allowed_applications:
|
||||
LOGGER.debug("Caching allowed application list")
|
||||
allowed_applications = self._get_allowed_applications(pagined_apps)
|
||||
allowed_applications = self._get_allowed_applications(paginated_apps)
|
||||
cache.set(
|
||||
user_app_cache_key(self.request.user.pk),
|
||||
allowed_applications,
|
||||
|
@ -617,6 +617,9 @@ class UserSourceConnection(SerializerModel, CreatedUpdatedModel):
|
||||
"""Get serializer for this model"""
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"User-source connection (user={self.user.username}, source={self.source.slug})"
|
||||
|
||||
class Meta:
|
||||
unique_together = (("user", "source"),)
|
||||
|
||||
|
@ -16,8 +16,9 @@ from authentik.core.models import Source, SourceUserMatchingModes, User, UserSou
|
||||
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION, PostUserEnrollmentStage
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.exceptions import FlowNonApplicableException
|
||||
from authentik.flows.models import Flow, Stage, in_memory_stage
|
||||
from authentik.flows.models import Flow, FlowToken, Stage, in_memory_stage
|
||||
from authentik.flows.planner import (
|
||||
PLAN_CONTEXT_IS_RESTORED,
|
||||
PLAN_CONTEXT_PENDING_USER,
|
||||
PLAN_CONTEXT_REDIRECT,
|
||||
PLAN_CONTEXT_SOURCE,
|
||||
@ -35,6 +36,8 @@ from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
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):
|
||||
"""Actions that can be decided based on the request
|
||||
@ -222,22 +225,43 @@ class SourceFlowManager:
|
||||
**kwargs,
|
||||
) -> HttpResponse:
|
||||
"""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(
|
||||
{
|
||||
# Since we authenticate the user by their token, they have no backend set
|
||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND: BACKEND_INBUILT,
|
||||
PLAN_CONTEXT_SSO: True,
|
||||
PLAN_CONTEXT_SOURCE: self.source,
|
||||
PLAN_CONTEXT_REDIRECT: final_redirect,
|
||||
PLAN_CONTEXT_SOURCES_CONNECTION: connection,
|
||||
}
|
||||
)
|
||||
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:
|
||||
return bad_request_message(
|
||||
self.request,
|
||||
|
@ -6,13 +6,13 @@ from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
||||
from authentik.api.authorization import OwnerFilter, OwnerSuperuserPermissions
|
||||
from authentik.core.api.groups import GroupMemberSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.providers.rac.api.endpoints import EndpointSerializer
|
||||
from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer
|
||||
from authentik.enterprise.providers.rac.models import ConnectionToken, Endpoint
|
||||
from authentik.enterprise.providers.rac.models import ConnectionToken
|
||||
|
||||
|
||||
class ConnectionTokenSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
||||
@ -23,7 +23,7 @@ class ConnectionTokenSerializer(EnterpriseRequiredMixin, ModelSerializer):
|
||||
user = GroupMemberSerializer(source="session.user", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Endpoint
|
||||
model = ConnectionToken
|
||||
fields = [
|
||||
"pk",
|
||||
"provider",
|
||||
@ -49,5 +49,5 @@ class ConnectionTokenViewSet(
|
||||
filterset_fields = ["endpoint", "session__user", "provider"]
|
||||
search_fields = ["endpoint__name", "provider__name"]
|
||||
ordering = ["endpoint__name", "provider__name"]
|
||||
permission_classes = [OwnerPermissions]
|
||||
permission_classes = [OwnerSuperuserPermissions]
|
||||
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||
|
@ -15,6 +15,7 @@ CELERY_BEAT_SCHEDULE = {
|
||||
TENANT_APPS = [
|
||||
"authentik.enterprise.audit",
|
||||
"authentik.enterprise.providers.rac",
|
||||
"authentik.enterprise.stages.source",
|
||||
]
|
||||
|
||||
MIDDLEWARE = ["authentik.enterprise.middleware.EnterpriseMiddleware"]
|
||||
|
0
authentik/enterprise/stages/__init__.py
Normal file
0
authentik/enterprise/stages/__init__.py
Normal file
0
authentik/enterprise/stages/source/__init__.py
Normal file
0
authentik/enterprise/stages/source/__init__.py
Normal file
38
authentik/enterprise/stages/source/api.py
Normal file
38
authentik/enterprise/stages/source/api.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""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"]
|
12
authentik/enterprise/stages/source/apps.py
Normal file
12
authentik/enterprise/stages/source/apps.py
Normal file
@ -0,0 +1,12 @@
|
||||
"""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
|
@ -0,0 +1,53 @@
|
||||
# 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",),
|
||||
),
|
||||
]
|
45
authentik/enterprise/stages/source/models.py
Normal file
45
authentik/enterprise/stages/source/models.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""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")
|
79
authentik/enterprise/stages/source/stage.py
Normal file
79
authentik/enterprise/stages/source/stage.py
Normal file
@ -0,0 +1,79 @@
|
||||
"""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()
|
99
authentik/enterprise/stages/source/tests.py
Normal file
99
authentik/enterprise/stages/source/tests.py
Normal file
@ -0,0 +1,99 @@
|
||||
"""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"))
|
5
authentik/enterprise/stages/source/urls.py
Normal file
5
authentik/enterprise/stages/source/urls.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""API URLs"""
|
||||
|
||||
from authentik.enterprise.stages.source.api import SourceStageViewSet
|
||||
|
||||
api_urlpatterns = [("stages/source", SourceStageViewSet)]
|
@ -12,7 +12,6 @@ from rest_framework.fields import (
|
||||
ChoiceField,
|
||||
DateTimeField,
|
||||
FloatField,
|
||||
ListField,
|
||||
SerializerMethodField,
|
||||
)
|
||||
from rest_framework.request import Request
|
||||
@ -21,6 +20,7 @@ from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.events.logs import LogEventSerializer
|
||||
from authentik.events.models import SystemTask, TaskStatus
|
||||
from authentik.rbac.decorators import permission_required
|
||||
|
||||
@ -39,7 +39,7 @@ class SystemTaskSerializer(ModelSerializer):
|
||||
duration = FloatField(read_only=True)
|
||||
|
||||
status = ChoiceField(choices=[(x.value, x.name) for x in TaskStatus])
|
||||
messages = ListField(child=CharField())
|
||||
messages = LogEventSerializer(many=True)
|
||||
|
||||
def get_full_name(self, instance: SystemTask) -> str:
|
||||
"""Get full name with UID"""
|
||||
|
82
authentik/events/logs.py
Normal file
82
authentik/events/logs.py
Normal file
@ -0,0 +1,82 @@
|
||||
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,6 +452,13 @@ class NotificationTransport(SerializerModel):
|
||||
|
||||
def send_email(self, notification: "Notification") -> list[str]:
|
||||
"""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: "
|
||||
context = {
|
||||
"key_value": {
|
||||
|
@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from structlog.stdlib import get_logger
|
||||
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 SystemTask as DBSystemTask
|
||||
from authentik.events.utils import sanitize_item
|
||||
@ -24,7 +25,7 @@ class SystemTask(TenantTask):
|
||||
save_on_success: bool
|
||||
|
||||
_status: TaskStatus
|
||||
_messages: list[str]
|
||||
_messages: list[LogEvent]
|
||||
|
||||
_uid: str | None
|
||||
# Precise start time from perf_counter
|
||||
@ -44,15 +45,20 @@ class SystemTask(TenantTask):
|
||||
"""Set UID, so in the case of an unexpected error its saved correctly"""
|
||||
self._uid = uid
|
||||
|
||||
def set_status(self, status: TaskStatus, *messages: str):
|
||||
def set_status(self, status: TaskStatus, *messages: LogEvent):
|
||||
"""Set result for current run, will overwrite previous result."""
|
||||
self._status = status
|
||||
self._messages = messages
|
||||
self._messages = list(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):
|
||||
"""Set result to error and save exception"""
|
||||
self._status = TaskStatus.ERROR
|
||||
self._messages = [exception_to_string(exception)]
|
||||
self._messages = [
|
||||
LogEvent(exception_to_string(exception), logger=self.__name__, log_level="error")
|
||||
]
|
||||
|
||||
def before_start(self, task_id, args, kwargs):
|
||||
self._start_precise = perf_counter()
|
||||
@ -98,8 +104,7 @@ class SystemTask(TenantTask):
|
||||
def on_failure(self, exc, task_id, args, kwargs, einfo):
|
||||
super().on_failure(exc, task_id, args, kwargs, einfo=einfo)
|
||||
if not self._status:
|
||||
self._status = TaskStatus.ERROR
|
||||
self._messages = exception_to_string(exc)
|
||||
self.set_error(exc)
|
||||
DBSystemTask.objects.update_or_create(
|
||||
name=self.__name__,
|
||||
uid=self._uid,
|
||||
|
@ -47,3 +47,4 @@ class FlowStageBindingViewSet(UsedByMixin, ModelViewSet):
|
||||
filterset_fields = "__all__"
|
||||
search_fields = ["stage__name"]
|
||||
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.utils import OpenApiResponse, extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import BooleanField, CharField, DictField, ListField, ReadOnlyField
|
||||
from rest_framework.fields import BooleanField, CharField, ReadOnlyField
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
from rest_framework.request import Request
|
||||
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.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import CacheSerializer, LinkSerializer, PassiveSerializer
|
||||
from authentik.events.utils import sanitize_dict
|
||||
from authentik.events.logs import LogEventSerializer
|
||||
from authentik.flows.api.flows_diagram import FlowDiagram, FlowDiagramSerializer
|
||||
from authentik.flows.exceptions import FlowNonApplicableException
|
||||
from authentik.flows.models import Flow
|
||||
@ -107,7 +107,7 @@ class FlowSetSerializer(FlowSerializer):
|
||||
class FlowImportResultSerializer(PassiveSerializer):
|
||||
"""Logs of an attempted flow import"""
|
||||
|
||||
logs = ListField(child=DictField(), read_only=True)
|
||||
logs = LogEventSerializer(many=True, read_only=True)
|
||||
success = BooleanField(read_only=True)
|
||||
|
||||
|
||||
@ -184,7 +184,7 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
|
||||
|
||||
importer = Importer.from_string(file.read().decode())
|
||||
valid, logs = importer.validate()
|
||||
import_response.initial_data["logs"] = [sanitize_dict(log) for log in logs]
|
||||
import_response.initial_data["logs"] = [LogEventSerializer(log).data for log in logs]
|
||||
import_response.initial_data["success"] = valid
|
||||
import_response.is_valid()
|
||||
if not valid:
|
||||
|
@ -13,6 +13,7 @@ from structlog.stdlib import get_logger
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.core.types import UserSettingSerializer
|
||||
from authentik.enterprise.apps import EnterpriseConfig
|
||||
from authentik.flows.api.flows import FlowSetSerializer
|
||||
from authentik.flows.models import ConfigurableStage, Stage
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
@ -75,6 +76,7 @@ class StageViewSet(
|
||||
"description": subclass.__doc__,
|
||||
"component": subclass().component,
|
||||
"model_name": subclass._meta.model_name,
|
||||
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
|
||||
}
|
||||
)
|
||||
data = sorted(data, key=lambda x: x["name"])
|
||||
|
@ -59,11 +59,11 @@ class FlowPlan:
|
||||
markers: list[StageMarker] = field(default_factory=list)
|
||||
|
||||
def append_stage(self, stage: Stage, marker: StageMarker | None = None):
|
||||
"""Append `stage` to all stages, optionally with stage marker"""
|
||||
"""Append `stage` to the end of the plan, optionally with stage marker"""
|
||||
return self.append(FlowStageBinding(stage=stage), marker)
|
||||
|
||||
def append(self, binding: FlowStageBinding, marker: StageMarker | None = None):
|
||||
"""Append `stage` to all stages, optionally with stage marker"""
|
||||
"""Append `stage` to the end of the plan, optionally with stage marker"""
|
||||
self.bindings.append(binding)
|
||||
self.markers.append(marker or StageMarker())
|
||||
|
||||
|
@ -53,6 +53,7 @@ class TestFlowInspector(APITestCase):
|
||||
"title": flow.title,
|
||||
"layout": "stacked",
|
||||
},
|
||||
"flow_designation": "authentication",
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"password_fields": False,
|
||||
"primary_action": "Log in",
|
||||
|
@ -450,7 +450,7 @@ class FlowExecutorView(APIView):
|
||||
return to_stage_response(self.request, challenge_view.get(self.request))
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel current execution and return a redirect"""
|
||||
"""Cancel current flow execution"""
|
||||
keys_to_delete = [
|
||||
SESSION_KEY_APPLICATION_PRE,
|
||||
SESSION_KEY_PLAN,
|
||||
@ -469,7 +469,7 @@ class FlowExecutorView(APIView):
|
||||
|
||||
|
||||
class CancelView(View):
|
||||
"""View which canels the currently active plan"""
|
||||
"""View which cancels the currently active plan"""
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""View which canels the currently active plan"""
|
||||
|
@ -3,9 +3,9 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from structlog.stdlib import get_logger
|
||||
from structlog.testing import capture_logs
|
||||
|
||||
from authentik import __version__, get_build_hash
|
||||
from authentik.events.logs import LogEvent, capture_logs
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.sentry import SentryIgnoredException
|
||||
from authentik.outposts.models import (
|
||||
@ -63,21 +63,21 @@ class BaseController:
|
||||
"""Called by scheduled task to reconcile deployment/service/etc"""
|
||||
raise NotImplementedError
|
||||
|
||||
def up_with_logs(self) -> list[str]:
|
||||
def up_with_logs(self) -> list[LogEvent]:
|
||||
"""Call .up() but capture all log output and return it."""
|
||||
with capture_logs() as logs:
|
||||
self.up()
|
||||
return [x["event"] for x in logs]
|
||||
return logs
|
||||
|
||||
def down(self):
|
||||
"""Handler to delete everything we've created"""
|
||||
raise NotImplementedError
|
||||
|
||||
def down_with_logs(self) -> list[str]:
|
||||
def down_with_logs(self) -> list[LogEvent]:
|
||||
"""Call .down() but capture all log output and return it."""
|
||||
with capture_logs() as logs:
|
||||
self.down()
|
||||
return [x["event"] for x in logs]
|
||||
return logs
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
@ -33,6 +33,8 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
|
||||
# priority than being updated.
|
||||
if current.spec.selector != reference.spec.selector:
|
||||
raise NeedsUpdate()
|
||||
if current.spec.type != reference.spec.type:
|
||||
raise NeedsUpdate()
|
||||
super().reconcile(current, reference)
|
||||
|
||||
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.incluster_config import load_incluster_config
|
||||
from kubernetes.config.kube_config import load_kube_config_from_dict
|
||||
from structlog.testing import capture_logs
|
||||
from urllib3.exceptions import HTTPError
|
||||
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.k8s.base import KubernetesObjectReconciler
|
||||
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
||||
@ -91,7 +91,7 @@ class KubernetesController(BaseController):
|
||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||
raise ControllerException(str(exc)) from exc
|
||||
|
||||
def up_with_logs(self) -> list[str]:
|
||||
def up_with_logs(self) -> list[LogEvent]:
|
||||
try:
|
||||
all_logs = []
|
||||
for reconcile_key in self.reconcile_order:
|
||||
@ -104,7 +104,9 @@ class KubernetesController(BaseController):
|
||||
continue
|
||||
reconciler = reconciler_cls(self)
|
||||
reconciler.up()
|
||||
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
|
||||
for log in logs:
|
||||
log.logger = reconcile_key.title()
|
||||
all_logs.extend(logs)
|
||||
return all_logs
|
||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||
raise ControllerException(str(exc)) from exc
|
||||
@ -122,7 +124,7 @@ class KubernetesController(BaseController):
|
||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||
raise ControllerException(str(exc)) from exc
|
||||
|
||||
def down_with_logs(self) -> list[str]:
|
||||
def down_with_logs(self) -> list[LogEvent]:
|
||||
try:
|
||||
all_logs = []
|
||||
for reconcile_key in self.reconcile_order:
|
||||
@ -135,7 +137,9 @@ class KubernetesController(BaseController):
|
||||
continue
|
||||
reconciler = reconciler_cls(self)
|
||||
reconciler.down()
|
||||
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
|
||||
for log in logs:
|
||||
log.logger = reconcile_key.title()
|
||||
all_logs.extend(logs)
|
||||
return all_logs
|
||||
except (OpenApiException, HTTPError, ServiceConnectionInvalid) as exc:
|
||||
raise ControllerException(str(exc)) from exc
|
||||
|
@ -149,10 +149,8 @@ def outpost_controller(
|
||||
if not controller_type:
|
||||
return
|
||||
with controller_type(outpost, outpost.service_connection) as controller:
|
||||
logs = getattr(controller, f"{action}_with_logs")()
|
||||
LOGGER.debug("---------------Outpost Controller logs starting----------------")
|
||||
for log in logs:
|
||||
LOGGER.debug(log)
|
||||
logs = getattr(controller, f"{action}_with_logs")()
|
||||
LOGGER.debug("-----------------Outpost Controller logs end-------------------")
|
||||
except (ControllerException, ServiceConnectionInvalid) as exc:
|
||||
self.set_error(exc)
|
||||
|
@ -1,10 +1,11 @@
|
||||
"""Serializer for policy execution"""
|
||||
|
||||
from rest_framework.fields import BooleanField, CharField, DictField, ListField
|
||||
from rest_framework.fields import BooleanField, CharField, ListField
|
||||
from rest_framework.relations import PrimaryKeyRelatedField
|
||||
|
||||
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||
from authentik.core.models import User
|
||||
from authentik.events.logs import LogEventSerializer
|
||||
|
||||
|
||||
class PolicyTestSerializer(PassiveSerializer):
|
||||
@ -19,4 +20,4 @@ class PolicyTestResultSerializer(PassiveSerializer):
|
||||
|
||||
passing = BooleanField()
|
||||
messages = ListField(child=CharField(), read_only=True)
|
||||
log_messages = ListField(child=DictField(), read_only=True)
|
||||
log_messages = LogEventSerializer(many=True, read_only=True)
|
||||
|
@ -11,12 +11,11 @@ from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
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.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import CacheSerializer, MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.events.utils import sanitize_dict
|
||||
from authentik.events.logs import LogEventSerializer, capture_logs
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
from authentik.policies.api.exec import PolicyTestResultSerializer, PolicyTestSerializer
|
||||
from authentik.policies.models import Policy, PolicyBinding
|
||||
@ -166,9 +165,9 @@ class PolicyViewSet(
|
||||
result = proc.execute()
|
||||
log_messages = []
|
||||
for log in logs:
|
||||
if log.get("process", "") == "PolicyProcess":
|
||||
if log.attributes.get("process", "") == "PolicyProcess":
|
||||
continue
|
||||
log_messages.append(sanitize_dict(log))
|
||||
log_messages.append(LogEventSerializer(log).data)
|
||||
result.log_messages = log_messages
|
||||
response = PolicyTestResultSerializer(result)
|
||||
return Response(response.data)
|
||||
|
@ -13,6 +13,7 @@ from authentik.events.context_processors.base import get_context_processors
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from authentik.core.models import User
|
||||
from authentik.events.logs import LogEvent
|
||||
from authentik.policies.models import PolicyBinding
|
||||
|
||||
LOGGER = get_logger()
|
||||
@ -74,7 +75,7 @@ class PolicyResult:
|
||||
source_binding: PolicyBinding | None
|
||||
source_results: list[PolicyResult] | None
|
||||
|
||||
log_messages: list[dict] | None
|
||||
log_messages: list[LogEvent] | None
|
||||
|
||||
def __init__(self, passing: bool, *messages: str):
|
||||
self.passing = passing
|
||||
|
@ -25,7 +25,7 @@ class OAuthDeviceCodeFinishChallengeResponse(ChallengeResponse):
|
||||
|
||||
|
||||
class OAuthDeviceCodeFinishStage(ChallengeStageView):
|
||||
"""Stage show at the end of a device flow"""
|
||||
"""Stage to finish the OAuth device code flow"""
|
||||
|
||||
response_class = OAuthDeviceCodeFinishChallengeResponse
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views import View
|
||||
from rest_framework.exceptions import ErrorDetail
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import CharField, IntegerField
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
@ -57,6 +57,7 @@ def validate_code(code: int, request: HttpRequest) -> HttpResponse | None:
|
||||
scope_descriptions = UserInfoView().get_scope_descriptions(token.scope, token.provider)
|
||||
planner = FlowPlanner(token.provider.authorization_flow)
|
||||
planner.allow_empty_flows = True
|
||||
planner.use_cache = False
|
||||
try:
|
||||
plan = planner.plan(
|
||||
request,
|
||||
@ -128,6 +129,13 @@ class OAuthDeviceCodeChallengeResponse(ChallengeResponse):
|
||||
code = IntegerField()
|
||||
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):
|
||||
"""Flow challenge for users to enter device codes"""
|
||||
@ -143,12 +151,4 @@ class OAuthDeviceCodeStage(ChallengeStageView):
|
||||
)
|
||||
|
||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
||||
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
|
||||
return response.validated_data["code"]
|
||||
|
@ -56,7 +56,10 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
||||
proxy_provider: ProxyProvider
|
||||
external_host_name = urlparse(proxy_provider.external_host)
|
||||
expected_hosts.append(external_host_name.hostname)
|
||||
if external_host_name.scheme == "https":
|
||||
if (
|
||||
external_host_name.scheme == "https"
|
||||
and self.controller.outpost.config.kubernetes_ingress_secret_name
|
||||
):
|
||||
expected_hosts_tls.append(external_host_name.hostname)
|
||||
expected_hosts.sort()
|
||||
expected_hosts_tls.sort()
|
||||
@ -116,7 +119,10 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
||||
):
|
||||
proxy_provider: ProxyProvider
|
||||
external_host_name = urlparse(proxy_provider.external_host)
|
||||
if external_host_name.scheme == "https":
|
||||
if (
|
||||
external_host_name.scheme == "https"
|
||||
and self.controller.outpost.config.kubernetes_ingress_secret_name
|
||||
):
|
||||
tls_hosts.append(external_host_name.hostname)
|
||||
if proxy_provider.mode in [
|
||||
ProxyMode.FORWARD_SINGLE,
|
||||
@ -160,13 +166,15 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
||||
rules.append(rule)
|
||||
tls_config = None
|
||||
if tls_hosts:
|
||||
tls_config = V1IngressTLS(
|
||||
hosts=tls_hosts,
|
||||
secret_name=self.controller.outpost.config.kubernetes_ingress_secret_name,
|
||||
)
|
||||
tls_config = [
|
||||
V1IngressTLS(
|
||||
hosts=tls_hosts,
|
||||
secret_name=self.controller.outpost.config.kubernetes_ingress_secret_name,
|
||||
)
|
||||
]
|
||||
spec = V1IngressSpec(
|
||||
rules=rules,
|
||||
tls=[tls_config],
|
||||
tls=tls_config,
|
||||
)
|
||||
if self.controller.outpost.config.kubernetes_ingress_class_name:
|
||||
spec.ingress_class_name = self.controller.outpost.config.kubernetes_ingress_class_name
|
||||
|
@ -196,8 +196,10 @@ if CONFIG.get_bool("redis.tls", False):
|
||||
_redis_protocol_prefix = "rediss://"
|
||||
_redis_celery_tls_requirements = f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
|
||||
_redis_url = (
|
||||
f"{_redis_protocol_prefix}:"
|
||||
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
|
||||
f"{_redis_protocol_prefix}"
|
||||
f"{quote_plus(CONFIG.get('redis.username'))}:"
|
||||
f"{quote_plus(CONFIG.get('redis.password'))}@"
|
||||
f"{quote_plus(CONFIG.get('redis.host'))}:"
|
||||
f"{CONFIG.get_int('redis.port')}"
|
||||
)
|
||||
|
||||
|
@ -77,6 +77,7 @@ class LDAPSourceSerializer(SourceSerializer):
|
||||
"group_object_filter",
|
||||
"group_membership_field",
|
||||
"object_uniqueness_field",
|
||||
"password_login_update_internal_password",
|
||||
"sync_users",
|
||||
"sync_users_password",
|
||||
"sync_groups",
|
||||
@ -118,6 +119,7 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
|
||||
"group_object_filter",
|
||||
"group_membership_field",
|
||||
"object_uniqueness_field",
|
||||
"password_login_update_internal_password",
|
||||
"sync_users",
|
||||
"sync_users_password",
|
||||
"sync_groups",
|
||||
|
@ -41,10 +41,11 @@ class LDAPBackend(InbuiltBackend):
|
||||
# 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
|
||||
if self.auth_user_by_bind(source, user, password):
|
||||
# Password given successfully binds to LDAP, so we save it in our Database
|
||||
LOGGER.debug("Updating user's password in DB", user=user)
|
||||
user.set_password(password, signal=False)
|
||||
user.save()
|
||||
if source.password_login_update_internal_password:
|
||||
# Password given successfully binds to LDAP, so we save it in our Database
|
||||
LOGGER.debug("Updating user's password in DB", user=user)
|
||||
user.set_password(password, signal=False)
|
||||
user.save()
|
||||
return user
|
||||
# Password doesn't match
|
||||
LOGGER.debug("Failed to bind, password invalid")
|
||||
|
@ -0,0 +1,29 @@
|
||||
# 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,6 +98,11 @@ class LDAPSource(Source):
|
||||
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_password = models.BooleanField(
|
||||
default=True,
|
||||
|
@ -47,7 +47,7 @@ class SourceType:
|
||||
def login_challenge(self, source: OAuthSource, request: HttpRequest) -> Challenge:
|
||||
"""Allow types to return custom challenges"""
|
||||
return RedirectChallenge(
|
||||
instance={
|
||||
data={
|
||||
"type": ChallengeTypes.REDIRECT.value,
|
||||
"to": reverse(
|
||||
"authentik_sources_oauth:oauth-client-login",
|
||||
|
@ -54,7 +54,7 @@ class OAuthCallback(OAuthClientMixin, View):
|
||||
raw_profile=exc.doc,
|
||||
).from_http(self.request)
|
||||
return self.handle_login_failure("Could not retrieve profile.")
|
||||
identifier = self.get_user_id(raw_info)
|
||||
identifier = self.get_user_id(info=raw_info)
|
||||
if identifier is None:
|
||||
return self.handle_login_failure("Could not determine id.")
|
||||
# Get or create access record
|
||||
@ -67,6 +67,7 @@ class OAuthCallback(OAuthClientMixin, View):
|
||||
)
|
||||
sfm.policy_context = {"oauth_userinfo": raw_info}
|
||||
return sfm.get_flow(
|
||||
raw_info=raw_info,
|
||||
access_token=self.token.get("access_token"),
|
||||
)
|
||||
|
||||
@ -116,6 +117,7 @@ class OAuthSourceFlowManager(SourceFlowManager):
|
||||
self,
|
||||
connection: UserOAuthSourceConnection,
|
||||
access_token: str | None = None,
|
||||
**_,
|
||||
) -> UserOAuthSourceConnection:
|
||||
"""Set the access_token on the connection"""
|
||||
connection.access_token = access_token
|
||||
|
@ -190,7 +190,7 @@ class SAMLSource(Source):
|
||||
def ui_login_button(self, request: HttpRequest) -> UILoginButton:
|
||||
return UILoginButton(
|
||||
challenge=RedirectChallenge(
|
||||
instance={
|
||||
data={
|
||||
"type": ChallengeTypes.REDIRECT.value,
|
||||
"to": reverse(
|
||||
"authentik_sources_saml:login",
|
||||
|
@ -234,12 +234,14 @@ class ResponseProcessor:
|
||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
|
||||
return self._handle_name_id_transient()
|
||||
|
||||
return SAMLSourceFlowManager(
|
||||
flow_manager = SAMLSourceFlowManager(
|
||||
self._source,
|
||||
self._http_request,
|
||||
name_id.text,
|
||||
delete_none_values(self.get_attributes()),
|
||||
)
|
||||
flow_manager.policy_context["saml_response"] = self._root
|
||||
return flow_manager
|
||||
|
||||
|
||||
class SAMLSourceFlowManager(SourceFlowManager):
|
||||
|
@ -120,7 +120,9 @@ def validate_challenge_code(code: str, stage_view: StageView, user: User) -> Dev
|
||||
stage=stage_view.executor.current_stage,
|
||||
device_class=DeviceClasses.TOTP.value,
|
||||
)
|
||||
raise ValidationError(_("Invalid Token"))
|
||||
raise ValidationError(
|
||||
_("Invalid Token. Please ensure the time on your device is accurate and try again.")
|
||||
)
|
||||
return device
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ class DummyChallenge(Challenge):
|
||||
"""Dummy challenge"""
|
||||
|
||||
component = CharField(default="ak-stage-dummy")
|
||||
name = CharField()
|
||||
|
||||
|
||||
class DummyChallengeResponse(ChallengeResponse):
|
||||
@ -35,5 +36,6 @@ class DummyStageView(ChallengeStageView):
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"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.utils.translation import gettext as _
|
||||
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
|
||||
from rest_framework.fields import BooleanField, CharField, DictField, ListField
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField, ListField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from sentry_sdk.hub import Hub
|
||||
|
||||
@ -66,6 +66,7 @@ class IdentificationChallenge(Challenge):
|
||||
user_fields = ListField(child=CharField(), allow_empty=True, allow_null=True)
|
||||
password_fields = BooleanField()
|
||||
application_pre = CharField(required=False)
|
||||
flow_designation = ChoiceField(FlowDesignation.choices)
|
||||
|
||||
enroll_url = CharField(required=False)
|
||||
recovery_url = CharField(required=False)
|
||||
@ -194,11 +195,12 @@ class IdentificationStageView(ChallengeStageView):
|
||||
challenge = IdentificationChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"primary_action": self.get_primary_action(),
|
||||
"component": "ak-stage-identification",
|
||||
"primary_action": self.get_primary_action(),
|
||||
"user_fields": current_stage.user_fields,
|
||||
"password_fields": bool(current_stage.password_stage),
|
||||
"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
|
||||
@ -237,7 +239,9 @@ class IdentificationStageView(ChallengeStageView):
|
||||
ui_login_button = source.ui_login_button(self.request)
|
||||
if ui_login_button:
|
||||
button = asdict(ui_login_button)
|
||||
button["challenge"] = ui_login_button.challenge.data
|
||||
source_challenge = ui_login_button.challenge
|
||||
source_challenge.is_valid()
|
||||
button["challenge"] = source_challenge.data
|
||||
ui_sources.append(button)
|
||||
challenge.initial_data["sources"] = ui_sources
|
||||
return challenge
|
||||
|
@ -12,6 +12,7 @@ from rest_framework.exceptions import ValidationError
|
||||
from authentik.core.middleware import SESSION_KEY_IMPERSONATE_USER
|
||||
from authentik.core.models import USER_ATTRIBUTE_SOURCES, User, UserSourceConnection, UserTypes
|
||||
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.stage import StageView
|
||||
from authentik.flows.views.executor import FlowExecutorView
|
||||
@ -47,7 +48,7 @@ class UserWriteStageView(StageView):
|
||||
# this is just a sanity check to ensure that is removed
|
||||
if parts[0] == "attributes":
|
||||
parts = parts[1:]
|
||||
set_path_in_dict(user.attributes, ".".join(parts), value)
|
||||
set_path_in_dict(user.attributes, ".".join(parts), sanitize_item(value))
|
||||
|
||||
def ensure_user(self) -> tuple[User | None, bool]:
|
||||
"""Ensure a user exists"""
|
||||
|
@ -87,11 +87,6 @@ class Tenant(TenantMixin, SerializerModel):
|
||||
raise IntegrityError("Cannot create schema named template")
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
if self.schema_name in ("public", "template"):
|
||||
raise IntegrityError("Cannot delete schema public or template")
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def serializer(self) -> Serializer:
|
||||
from authentik.tenants.api.tenants import TenantSerializer
|
||||
|
14
authentik/tenants/signals.py
Normal file
14
authentik/tenants/signals.py
Normal file
@ -0,0 +1,14 @@
|
||||
"""authentik tenants signals"""
|
||||
|
||||
from django.db import models
|
||||
from django.db.models.signals import pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django_tenants.utils import get_public_schema_name
|
||||
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Tenant)
|
||||
def tenants_ensure_no_default_delete(sender, instance: Tenant, **kwargs):
|
||||
if instance.schema_name == get_public_schema_name():
|
||||
raise models.ProtectedError("Cannot delete schema public", instance)
|
@ -2594,6 +2594,43 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"required": [
|
||||
@ -3257,6 +3294,7 @@
|
||||
"authentik.enterprise",
|
||||
"authentik.enterprise.audit",
|
||||
"authentik.enterprise.providers.rac",
|
||||
"authentik.enterprise.stages.source",
|
||||
"authentik.events"
|
||||
],
|
||||
"title": "App",
|
||||
@ -3338,6 +3376,7 @@
|
||||
"authentik_providers_rac.racprovider",
|
||||
"authentik_providers_rac.endpoint",
|
||||
"authentik_providers_rac.racpropertymapping",
|
||||
"authentik_stages_source.sourcestage",
|
||||
"authentik_events.event",
|
||||
"authentik_events.notificationtransport",
|
||||
"authentik_events.notification",
|
||||
@ -4308,6 +4347,11 @@
|
||||
"title": "Object uniqueness field",
|
||||
"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": {
|
||||
"type": "boolean",
|
||||
"title": "Sync users"
|
||||
@ -8018,6 +8062,109 @@
|
||||
},
|
||||
"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": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
32
go.mod
32
go.mod
@ -10,8 +10,8 @@ require (
|
||||
github.com/getsentry/sentry-go v0.27.0
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-openapi/runtime v0.27.2
|
||||
github.com/go-openapi/strfmt v0.22.2
|
||||
github.com/go-openapi/runtime v0.28.0
|
||||
github.com/go-openapi/strfmt v0.23.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
@ -30,7 +30,7 @@ require (
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2024022.1
|
||||
goauthentik.io/api/v3 v3.2024022.7
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.18.0
|
||||
golang.org/x/sync v0.6.0
|
||||
@ -49,16 +49,16 @@ require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 // indirect
|
||||
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.22.2 // indirect
|
||||
github.com/go-openapi/errors v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/loads v0.21.5 // indirect
|
||||
github.com/go-openapi/spec v0.20.14 // indirect
|
||||
github.com/go-openapi/swag v0.22.9 // indirect
|
||||
github.com/go-openapi/validate v0.23.0 // indirect
|
||||
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||
github.com/go-openapi/errors v0.22.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/loads v0.22.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@ -72,15 +72,15 @@ require (
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
68
go.sum
68
go.sum
@ -87,30 +87,30 @@ github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I7
|
||||
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/analysis v0.22.2 h1:ZBmNoP2h5omLKr/srIC9bfqrUGzT6g6gNv03HE9Vpj0=
|
||||
github.com/go-openapi/analysis v0.22.2/go.mod h1:pDF4UbZsQTo/oNuRfAWWd4dAh4yuYf//LYorPTjrpvo=
|
||||
github.com/go-openapi/errors v0.21.1 h1:rVisxQPdETctjlYntm0Ek4dKf68nAQocCloCT50vWuI=
|
||||
github.com/go-openapi/errors v0.21.1/go.mod h1:LyiY9bgc7AVVh6wtVvMYEyoj3KJYNoRw92mmvnMWgj8=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
|
||||
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
|
||||
github.com/go-openapi/loads v0.21.5 h1:jDzF4dSoHw6ZFADCGltDb2lE4F6De7aWSpe+IcsRzT0=
|
||||
github.com/go-openapi/loads v0.21.5/go.mod h1:PxTsnFBoBe+z89riT+wYt3prmSBP6GDAQh2l9H1Flz8=
|
||||
github.com/go-openapi/runtime v0.27.2 h1:AOvytl8s9DzL7B27r6dZ4sqjVOJT6/3LzKeZoDIAh+g=
|
||||
github.com/go-openapi/runtime v0.27.2/go.mod h1:a5AkfzISU/Iwq51ZiQLM+oNRDwqC9RtlSt57xUSyZhg=
|
||||
github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do=
|
||||
github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw=
|
||||
github.com/go-openapi/strfmt v0.22.2 h1:DPYOrm6gexCfZZfXUaXFS4+Jw6HAaIIG0SZ5630f8yw=
|
||||
github.com/go-openapi/strfmt v0.22.2/go.mod h1:HB/b7TCm91rno75Dembc1dFW/0FPLk5CEXsoF9ReNc4=
|
||||
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
|
||||
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
|
||||
github.com/go-openapi/validate v0.23.0 h1:2l7PJLzCis4YUGEoW6eoQw3WhyM65WSIcjX6SQnlfDw=
|
||||
github.com/go-openapi/validate v0.23.0/go.mod h1:EeiAZ5bmpSIOJV1WLfyYF9qp/B1ZgSaEpHTJHtN5cbE=
|
||||
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
|
||||
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
|
||||
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
|
||||
github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
|
||||
github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
|
||||
github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
|
||||
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
|
||||
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@ -270,18 +270,18 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM=
|
||||
go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0=
|
||||
go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc=
|
||||
go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o=
|
||||
go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE=
|
||||
go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ=
|
||||
go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ=
|
||||
go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
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/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
goauthentik.io/api/v3 v3.2024022.1 h1:ydYi3X/OSnu4LumUN+oCe6vvGDXil1Xn186hC9FQb4Q=
|
||||
goauthentik.io/api/v3 v3.2024022.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2024022.7 h1:VR9OmcZvTzPSjit2Dx2EoHrLc9v9XRyjPXNpnGISWWM=
|
||||
goauthentik.io/api/v3 v3.2024022.7/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -556,8 +556,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
@ -10,12 +10,17 @@ const CodePasswordSeparator = ";"
|
||||
|
||||
var alphaNum = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
|
||||
|
||||
// CheckPasswordInlineMFA For protocols that only support username/password, check if the password
|
||||
// contains the TOTP code
|
||||
func (fe *FlowExecutor) CheckPasswordInlineMFA() {
|
||||
password := fe.Answers[StagePassword]
|
||||
// We already have an authenticator answer
|
||||
if fe.Answers[StageAuthenticatorValidate] != "" {
|
||||
// Sets the secret answers for the flow executor for protocols that only support username/password
|
||||
// according to used options
|
||||
func (fe *FlowExecutor) SetSecrets(password string, mfaCodeBased bool) {
|
||||
if fe.Answers[StageAuthenticatorValidate] != "" || fe.Answers[StagePassword] != "" {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
// password doesn't contain the separator
|
||||
|
@ -23,10 +23,7 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
|
||||
fe.Answers[flow.StageIdentification] = username
|
||||
fe.Answers[flow.StagePassword] = req.BindPW
|
||||
if db.si.GetMFASupport() {
|
||||
fe.CheckPasswordInlineMFA()
|
||||
}
|
||||
fe.SetSecrets(req.BindPW, db.si.GetMFASupport())
|
||||
|
||||
passed, err := fe.Execute()
|
||||
flags := flags.UserFlags{
|
||||
|
@ -35,7 +35,7 @@ type ProviderInstance struct {
|
||||
cert *tls.Certificate
|
||||
certUUID string
|
||||
outpostName string
|
||||
outpostPk int32
|
||||
providerPk int32
|
||||
searchAllowedGroups []*strfmt.UUID
|
||||
boundUsersMutex *sync.RWMutex
|
||||
boundUsers map[string]*flags.UserFlags
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
|
||||
func (ls *LDAPServer) getCurrentProvider(pk int32) *ProviderInstance {
|
||||
for _, p := range ls.providers {
|
||||
if p.outpostPk == pk {
|
||||
if p.providerPk == pk {
|
||||
return p
|
||||
}
|
||||
}
|
||||
@ -83,7 +83,7 @@ func (ls *LDAPServer) Refresh() error {
|
||||
gidStartNumber: provider.GetGidStartNumber(),
|
||||
mfaSupport: provider.GetMfaSupport(),
|
||||
outpostName: ls.ac.Outpost.Name,
|
||||
outpostPk: provider.Pk,
|
||||
providerPk: provider.Pk,
|
||||
}
|
||||
if kp := provider.Certificate.Get(); kp != nil {
|
||||
err := ls.cs.AddKeypair(*kp)
|
||||
|
@ -56,7 +56,7 @@ func TestProxy_Redirect_Subdirectory(t *testing.T) {
|
||||
loc, _ := rr.Result().Location()
|
||||
assert.Equal(
|
||||
t,
|
||||
"https://ext.t.goauthentik.io/subdir/outpost.goauthentik.io/start?rd=https%3A%2F%2Fext.t.goauthentik.io%2Ffoo",
|
||||
"https://ext.t.goauthentik.io/subdir/outpost.goauthentik.io/start?rd=https%3A%2F%2Fext.t.goauthentik.io%2Fsubdir%2Ffoo",
|
||||
loc.String(),
|
||||
)
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
|
||||
}
|
||||
if a.isEmbedded {
|
||||
client := redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
|
||||
// Username: config.Get().Redis.Password,
|
||||
Addr: fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
|
||||
Username: config.Get().Redis.Username,
|
||||
Password: config.Get().Redis.Password,
|
||||
DB: config.Get().Redis.DB,
|
||||
})
|
||||
|
@ -3,7 +3,6 @@ package application
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -11,22 +10,12 @@ import (
|
||||
"goauthentik.io/internal/outpost/proxyv2/constants"
|
||||
)
|
||||
|
||||
func urlPathSet(originalUrl string, newPath string) string {
|
||||
u, err := url.Parse(originalUrl)
|
||||
if err != nil {
|
||||
return originalUrl
|
||||
}
|
||||
u.Path = newPath
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func urlJoin(originalUrl string, newPath string) string {
|
||||
u, err := url.Parse(originalUrl)
|
||||
u, err := url.JoinPath(originalUrl, newPath)
|
||||
if err != nil {
|
||||
return originalUrl
|
||||
}
|
||||
u.Path = path.Join(u.Path, newPath)
|
||||
return u.String()
|
||||
return u
|
||||
}
|
||||
|
||||
func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) {
|
||||
@ -46,7 +35,7 @@ func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
redirectUrl := urlPathSet(a.proxyConfig.ExternalHost, r.URL.Path)
|
||||
redirectUrl := urlJoin(a.proxyConfig.ExternalHost, r.URL.Path)
|
||||
|
||||
if a.Mode() == api.PROXYMODE_FORWARD_DOMAIN {
|
||||
dom := strings.TrimPrefix(*a.proxyConfig.CookieDomain, ".")
|
||||
|
@ -6,8 +6,10 @@ import (
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/outpost/ldap/flags"
|
||||
)
|
||||
|
||||
func parseCIDRs(raw string) []*net.IPNet {
|
||||
@ -29,6 +31,25 @@ func parseCIDRs(raw string) []*net.IPNet {
|
||||
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 {
|
||||
outposts, _, err := rs.ac.Client.OutpostsApi.OutpostsRadiusList(context.Background()).Execute()
|
||||
if err != nil {
|
||||
@ -37,17 +58,33 @@ func (rs *RadiusServer) Refresh() error {
|
||||
if len(outposts.Results) < 1 {
|
||||
return errors.New("no radius provider defined")
|
||||
}
|
||||
invalidationFlow := rs.getInvalidationFlow()
|
||||
providers := make([]*ProviderInstance, len(outposts.Results))
|
||||
for idx, provider := range outposts.Results {
|
||||
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{
|
||||
SharedSecret: []byte(provider.GetSharedSecret()),
|
||||
ClientNetworks: parseCIDRs(provider.GetClientNetworks()),
|
||||
MFASupport: provider.GetMfaSupport(),
|
||||
appSlug: provider.ApplicationSlug,
|
||||
flowSlug: provider.AuthFlowSlug,
|
||||
s: rs,
|
||||
log: logger,
|
||||
SharedSecret: []byte(provider.GetSharedSecret()),
|
||||
ClientNetworks: parseCIDRs(provider.GetClientNetworks()),
|
||||
MFASupport: provider.GetMfaSupport(),
|
||||
appSlug: provider.ApplicationSlug,
|
||||
authenticationFlowSlug: provider.AuthFlowSlug,
|
||||
invalidationFlowSlug: invalidationFlow,
|
||||
s: rs,
|
||||
log: logger,
|
||||
providerPk: provider.Pk,
|
||||
boundUsersMutex: usersMutex,
|
||||
boundUsers: users,
|
||||
}
|
||||
}
|
||||
rs.providers = providers
|
||||
|
@ -4,15 +4,17 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/outpost/flow"
|
||||
"goauthentik.io/internal/outpost/ldap/flags"
|
||||
"goauthentik.io/internal/outpost/radius/metrics"
|
||||
"layeh.com/radius"
|
||||
"layeh.com/radius/rfc2865"
|
||||
"layeh.com/radius/rfc2866"
|
||||
)
|
||||
|
||||
func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusRequest) {
|
||||
username := rfc2865.UserName_GetString(r.Packet)
|
||||
|
||||
fe := flow.NewFlowExecutor(r.Context(), r.pi.flowSlug, r.pi.s.ac.Client.GetConfig(), log.Fields{
|
||||
fe := flow.NewFlowExecutor(r.Context(), r.pi.authenticationFlowSlug, r.pi.s.ac.Client.GetConfig(), log.Fields{
|
||||
"username": username,
|
||||
"client": r.RemoteAddr(),
|
||||
"requestId": r.ID(),
|
||||
@ -21,10 +23,7 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
||||
|
||||
fe.Answers[flow.StageIdentification] = username
|
||||
fe.Answers[flow.StagePassword] = rfc2865.UserPassword_GetString(r.Packet)
|
||||
if r.pi.MFASupport {
|
||||
fe.CheckPasswordInlineMFA()
|
||||
}
|
||||
fe.SetSecrets(rfc2865.UserPassword_GetString(r.Packet), r.pi.MFASupport)
|
||||
|
||||
passed, err := fe.Execute()
|
||||
if err != nil {
|
||||
@ -67,5 +66,28 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||
}).Inc()
|
||||
return
|
||||
}
|
||||
_ = w.Write(r.Response(radius.CodeAccessAccept))
|
||||
// Get user info to store in context
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
54
internal/outpost/radius/handle_disconnect_request.go
Normal file
54
internal/outpost/radius/handle_disconnect_request.go
Normal file
@ -0,0 +1,54 @@
|
||||
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,7 +74,12 @@ func (rs *RadiusServer) ServeRADIUS(w radius.ResponseWriter, r *radius.Request)
|
||||
}
|
||||
nr.pi = pi
|
||||
|
||||
if nr.Code == radius.CodeAccessRequest {
|
||||
switch nr.Code {
|
||||
case radius.CodeAccessRequest:
|
||||
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,20 +9,25 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/ldap/flags"
|
||||
"goauthentik.io/internal/outpost/radius/metrics"
|
||||
|
||||
"layeh.com/radius"
|
||||
)
|
||||
|
||||
type ProviderInstance struct {
|
||||
ClientNetworks []*net.IPNet
|
||||
SharedSecret []byte
|
||||
MFASupport bool
|
||||
ClientNetworks []*net.IPNet
|
||||
SharedSecret []byte
|
||||
MFASupport bool
|
||||
boundUsersMutex *sync.RWMutex
|
||||
boundUsers map[string]*flags.UserFlags
|
||||
providerPk int32
|
||||
|
||||
appSlug string
|
||||
flowSlug string
|
||||
s *RadiusServer
|
||||
log *log.Entry
|
||||
appSlug string
|
||||
authenticationFlowSlug string
|
||||
invalidationFlowSlug string
|
||||
s *RadiusServer
|
||||
log *log.Entry
|
||||
}
|
||||
|
||||
type RadiusServer struct {
|
||||
|
@ -14,26 +14,27 @@ import (
|
||||
)
|
||||
|
||||
func (ws *WebServer) configureStatic() {
|
||||
statRouter := ws.lh.NewRoute().Subrouter()
|
||||
statRouter.Use(ws.staticHeaderMiddleware)
|
||||
indexLessRouter := statRouter.NewRoute().Subrouter()
|
||||
indexLessRouter.Use(web.DisableIndex)
|
||||
distFs := http.FileServer(http.Dir("./web/dist"))
|
||||
distHandler := http.StripPrefix("/static/dist/", distFs)
|
||||
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)
|
||||
staticRouter := ws.lh.NewRoute().Subrouter()
|
||||
staticRouter.Use(ws.staticHeaderMiddleware)
|
||||
staticRouter.Use(web.DisableIndex)
|
||||
|
||||
// Prevent font-loading issues on safari, which loads fonts relatively to the URL the browser is on
|
||||
indexLessRouter.PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
distFs := http.FileServer(http.Dir("./web/dist"))
|
||||
authentikHandler := http.StripPrefix("/static/authentik/", http.FileServer(http.Dir("./web/authentik")))
|
||||
|
||||
// Root file paths, from which they should be accessed
|
||||
staticRouter.PathPrefix("/static/dist/").Handler(http.StripPrefix("/static/dist/", distFs))
|
||||
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)
|
||||
|
||||
web.DisableIndex(http.StripPrefix(fmt.Sprintf("/if/flow/%s", vars["flow_slug"]), distFs)).ServeHTTP(rw, r)
|
||||
})
|
||||
indexLessRouter.PathPrefix("/if/admin/assets").Handler(http.StripPrefix("/if/admin", distFs))
|
||||
indexLessRouter.PathPrefix("/if/user/assets").Handler(http.StripPrefix("/if/user", distFs))
|
||||
indexLessRouter.PathPrefix("/if/rac/{app_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
staticRouter.PathPrefix("/if/admin/assets").Handler(http.StripPrefix("/if/admin", distFs))
|
||||
staticRouter.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) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
web.DisableIndex(http.StripPrefix(fmt.Sprintf("/if/rac/%s", vars["app_slug"]), distFs)).ServeHTTP(rw, r)
|
||||
@ -42,12 +43,13 @@ func (ws *WebServer) configureStatic() {
|
||||
// Media files, if backend is file
|
||||
if config.Get().Storage.Media.Backend == "file" {
|
||||
fsMedia := http.FileServer(http.Dir(config.Get().Storage.Media.File.Path))
|
||||
indexLessRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fsMedia))
|
||||
staticRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fsMedia))
|
||||
}
|
||||
|
||||
statRouter.PathPrefix("/if/help/").Handler(http.StripPrefix("/if/help/", helpHandler))
|
||||
statRouter.PathPrefix("/help").Handler(http.RedirectHandler("/if/help/", http.StatusMovedPermanently))
|
||||
staticRouter.PathPrefix("/if/help/").Handler(http.StripPrefix("/if/help/", http.FileServer(http.Dir("./website/help/"))))
|
||||
staticRouter.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) {
|
||||
rw.Header()["Content-Type"] = []string{"text/plain"}
|
||||
rw.WriteHeader(200)
|
||||
|
@ -64,6 +64,7 @@ def release_lock(cursor: Cursor):
|
||||
"""Release database lock"""
|
||||
if not LOCKED:
|
||||
return
|
||||
LOGGER.info("releasing database lock")
|
||||
cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,))
|
||||
|
||||
|
||||
|
12
lifecycle/system_migrations/template_schema.py
Normal file
12
lifecycle/system_migrations/template_schema.py
Normal file
@ -0,0 +1,12 @@
|
||||
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,6 +116,7 @@ class Migration(BaseMigration):
|
||||
host=CONFIG.get("redis.host"),
|
||||
port=6379,
|
||||
db=db,
|
||||
username=CONFIG.get("redis.username"),
|
||||
password=CONFIG.get("redis.password"),
|
||||
)
|
||||
redis.flushall()
|
||||
|
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-01 00:07+0000\n"
|
||||
"POT-Creation-Date: 2024-03-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2024\n"
|
||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||
@ -802,6 +802,10 @@ msgstr "Jeton du flux"
|
||||
msgid "Flow Tokens"
|
||||
msgstr "Jetons du flux"
|
||||
|
||||
#: authentik/flows/views/executor.py
|
||||
msgid "Invalid next URL"
|
||||
msgstr "URL suivante invalide"
|
||||
|
||||
#: authentik/lib/utils/time.py
|
||||
#, python-format
|
||||
msgid "%(value)s is not in the correct format of 'hours=3;minutes=1'."
|
||||
|
344
poetry.lock
generated
344
poetry.lock
generated
@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
@ -317,13 +317,13 @@ visualize = ["Twisted (>=16.1.1)", "graphviz (>0.5.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "bandit"
|
||||
version = "1.7.7"
|
||||
version = "1.7.8"
|
||||
description = "Security oriented static analyser for python code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"},
|
||||
{file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"},
|
||||
{file = "bandit-1.7.8-py3-none-any.whl", hash = "sha256:509f7af645bc0cd8fd4587abc1a038fc795636671ee8204d502b933aee44f381"},
|
||||
{file = "bandit-1.7.8.tar.gz", hash = "sha256:36de50f720856ab24a24dbaa5fee2c66050ed97c1477e0a1159deab1775eab6b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -334,6 +334,7 @@ stevedore = ">=1.20.0"
|
||||
|
||||
[package.extras]
|
||||
baseline = ["GitPython (>=3.1.30)"]
|
||||
sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"]
|
||||
test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"]
|
||||
toml = ["tomli (>=1.1.0)"]
|
||||
yaml = ["PyYAML"]
|
||||
@ -391,33 +392,33 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.2.0"
|
||||
version = "24.3.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"},
|
||||
{file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"},
|
||||
{file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"},
|
||||
{file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"},
|
||||
{file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"},
|
||||
{file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"},
|
||||
{file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"},
|
||||
{file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"},
|
||||
{file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"},
|
||||
{file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"},
|
||||
{file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"},
|
||||
{file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"},
|
||||
{file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"},
|
||||
{file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"},
|
||||
{file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"},
|
||||
{file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"},
|
||||
{file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"},
|
||||
{file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"},
|
||||
{file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"},
|
||||
{file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"},
|
||||
{file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"},
|
||||
{file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"},
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
|
||||
{file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
|
||||
{file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
|
||||
{file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
|
||||
{file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
|
||||
{file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
|
||||
{file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
|
||||
{file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
|
||||
{file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
|
||||
{file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
|
||||
{file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
|
||||
{file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
|
||||
{file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -454,19 +455,19 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
||||
|
||||
[[package]]
|
||||
name = "botocore"
|
||||
version = "1.34.15"
|
||||
version = "1.34.63"
|
||||
description = "Low-level, data-driven core of boto 3."
|
||||
optional = false
|
||||
python-versions = ">= 3.8"
|
||||
files = [
|
||||
{file = "botocore-1.34.15-py3-none-any.whl", hash = "sha256:16bcf871e67ef0177593f06e9e5bae4db51c9a9a2e953cb14feeb42d53441a85"},
|
||||
{file = "botocore-1.34.15.tar.gz", hash = "sha256:c3c3404962a6d9d5e1634bd70ed53b8eff1ff17ee9d7a6240e9e8c94db48ad6f"},
|
||||
{file = "botocore-1.34.63-py3-none-any.whl", hash = "sha256:8a6cbc3a5c5988725c00815f8f7f6baf81980b19d9a2ee414b031e726759dba9"},
|
||||
{file = "botocore-1.34.63.tar.gz", hash = "sha256:2237743fc3ed68319bc358b451e7c13a02110242b1522b839806fd64fcee45fb"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
jmespath = ">=0.7.1,<2.0.0"
|
||||
python-dateutil = ">=2.1,<3.0.0"
|
||||
urllib3 = {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""}
|
||||
urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
crt = ["awscrt (==0.19.19)"]
|
||||
@ -918,63 +919,63 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.4.3"
|
||||
version = "7.4.4"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
|
||||
{file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
|
||||
{file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
|
||||
{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.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.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
|
||||
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
|
||||
{file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
|
||||
{file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
|
||||
{file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
|
||||
{file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
|
||||
{file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
|
||||
{file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
|
||||
{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.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.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
|
||||
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
|
||||
{file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
|
||||
{file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
|
||||
{file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
|
||||
{file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
|
||||
{file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
|
||||
{file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
|
||||
{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.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.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
|
||||
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
|
||||
{file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
|
||||
{file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
|
||||
{file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
|
||||
{file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
|
||||
{file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
|
||||
{file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
|
||||
{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.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.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
|
||||
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
|
||||
{file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
|
||||
{file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
|
||||
{file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
|
||||
{file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
|
||||
{file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
|
||||
{file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
|
||||
{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.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.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
|
||||
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
|
||||
{file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
|
||||
{file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
|
||||
{file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
|
||||
{file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
|
||||
{file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
|
||||
{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.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.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
|
||||
{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.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.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
|
||||
{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.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.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
|
||||
{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.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.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
|
||||
{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.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.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
|
||||
{file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
|
||||
{file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -1141,17 +1142,17 @@ bcrypt = ["bcrypt"]
|
||||
|
||||
[[package]]
|
||||
name = "django-filter"
|
||||
version = "23.5"
|
||||
version = "24.2"
|
||||
description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "django-filter-23.5.tar.gz", hash = "sha256:67583aa43b91fe8c49f74a832d95f4d8442be628fd4c6d65e9f811f5153a4e5c"},
|
||||
{file = "django_filter-23.5-py3-none-any.whl", hash = "sha256:99122a201d83860aef4fe77758b69dda913e874cc5e0eaa50a86b0b18d708400"},
|
||||
{file = "django-filter-24.2.tar.gz", hash = "sha256:48e5fc1da3ccd6ca0d5f9bb550973518ce977a4edde9d2a8a154a7f4f0b9f96e"},
|
||||
{file = "django_filter-24.2-py3-none-any.whl", hash = "sha256:df2ee9857e18d38bed203c8745f62a803fa0f31688c9fe6f8e868120b1848e48"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=3.2"
|
||||
Django = ">=4.2"
|
||||
|
||||
[[package]]
|
||||
name = "django-guardian"
|
||||
@ -1388,13 +1389,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "duo-client"
|
||||
version = "5.2.0"
|
||||
version = "5.3.0"
|
||||
description = "Reference client for Duo Security APIs"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "duo_client-5.2.0-py3-none-any.whl", hash = "sha256:da3237e34300665c40ba5215f1e6656fec1a0136295917541aa973e7fcbf027e"},
|
||||
{file = "duo_client-5.2.0.tar.gz", hash = "sha256:f82361740792b06303f9721e7ba593916080461769396b4f73c0502c0bfcee44"},
|
||||
{file = "duo_client-5.3.0-py3-none-any.whl", hash = "sha256:85614bb684cef96285268aef0c1e858df939f6e8a190fb2c707d700bb0215766"},
|
||||
{file = "duo_client-5.3.0.tar.gz", hash = "sha256:afa5ef98a42f06965a2702ca41dba9c85c483abd945e0a440f0ec4871b7593bf"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1711,13 +1712,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "7.0.2"
|
||||
version = "7.1.0"
|
||||
description = "Read metadata from Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"},
|
||||
{file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"},
|
||||
{file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
|
||||
{file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1726,7 +1727,7 @@ zipp = ">=0.5"
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
perf = ["ipython"]
|
||||
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)"]
|
||||
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)"]
|
||||
|
||||
[[package]]
|
||||
name = "incremental"
|
||||
@ -2605,13 +2606,13 @@ attrs = ">=19.2.0"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
version = "24.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
|
||||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2693,13 +2694,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
|
||||
{file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
|
||||
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
|
||||
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -2847,13 +2848,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.6.3"
|
||||
version = "2.6.4"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"},
|
||||
{file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"},
|
||||
{file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"},
|
||||
{file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3078,23 +3079,23 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.0.2"
|
||||
version = "8.1.1"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"},
|
||||
{file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"},
|
||||
{file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"},
|
||||
{file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=1.3.0,<2.0"
|
||||
pluggy = ">=1.4,<2.0"
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-django"
|
||||
@ -3331,32 +3332,30 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests-mock"
|
||||
version = "1.11.0"
|
||||
version = "1.12.1"
|
||||
description = "Mock out responses from the requests package"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "requests-mock-1.11.0.tar.gz", hash = "sha256:ef10b572b489a5f28e09b708697208c4a3b2b89ef80a9f01584340ea357ec3c4"},
|
||||
{file = "requests_mock-1.11.0-py2.py3-none-any.whl", hash = "sha256:f7fae383f228633f6bececebdab236c478ace2284d6292c6e7e2867b9ab74d15"},
|
||||
{file = "requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"},
|
||||
{file = "requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
requests = ">=2.3,<3"
|
||||
six = "*"
|
||||
requests = ">=2.22,<3"
|
||||
|
||||
[package.extras]
|
||||
fixture = ["fixtures"]
|
||||
test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"]
|
||||
|
||||
[[package]]
|
||||
name = "requests-oauthlib"
|
||||
version = "1.3.1"
|
||||
version = "2.0.0"
|
||||
description = "OAuthlib authentication support for Requests."
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
python-versions = ">=3.4"
|
||||
files = [
|
||||
{file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"},
|
||||
{file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"},
|
||||
{file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"},
|
||||
{file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3508,28 +3507,28 @@ pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.3.1"
|
||||
version = "0.3.4"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6b82e3937d0d76554cd5796bc3342a7d40de44494d29ff490022d7a52c501744"},
|
||||
{file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ae7954c8f692b70e6a206087ae3988acc9295d84c550f8d90b66c62424c16771"},
|
||||
{file = "ruff-0.3.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b730f56ccf91225da0f06cfe421e83b8cc27b2a79393db9c3df02ed7e2bbc01"},
|
||||
{file = "ruff-0.3.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c78bfa85637668f47bd82aa2ae17de2b34221ac23fea30926f6409f9e37fc927"},
|
||||
{file = "ruff-0.3.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6abaad602d6e6daaec444cbf4d9364df0a783e49604c21499f75bb92237d4af"},
|
||||
{file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0c21b6914c3c9a25a59497cbb1e5b6c2d8d9beecc9b8e03ee986e24eee072e"},
|
||||
{file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434c3fc72e6311c85cd143c4c448b0e60e025a9ac1781e63ba222579a8c29200"},
|
||||
{file = "ruff-0.3.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78a7025e6312cbba496341da5062e7cdd47d95f45c1b903e635cdeb1ba5ec2b9"},
|
||||
{file = "ruff-0.3.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b02bb46f1a79b0c1fa93f6495bc7e77e4ef76e6c28995b4974a20ed09c0833"},
|
||||
{file = "ruff-0.3.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11b5699c42f7d0b771c633d620f2cb22e727fb226273aba775a91784a9ed856c"},
|
||||
{file = "ruff-0.3.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:54e5dca3e411772b51194b3102b5f23b36961e8ede463776b289b78180df71a0"},
|
||||
{file = "ruff-0.3.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:951efb610c5844e668bbec4f71cf704f8645cf3106e13f283413969527ebfded"},
|
||||
{file = "ruff-0.3.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:09c7333b25e983aabcf6e38445252cff0b4745420fc3bda45b8fce791cc7e9ce"},
|
||||
{file = "ruff-0.3.1-py3-none-win32.whl", hash = "sha256:d937f9b99ebf346e0606c3faf43c1e297a62ad221d87ef682b5bdebe199e01f6"},
|
||||
{file = "ruff-0.3.1-py3-none-win_amd64.whl", hash = "sha256:c0318a512edc9f4e010bbaab588b5294e78c5cdc9b02c3d8ab2d77c7ae1903e3"},
|
||||
{file = "ruff-0.3.1-py3-none-win_arm64.whl", hash = "sha256:d3b60e44240f7e903e6dbae3139a65032ea4c6f2ad99b6265534ff1b83c20afa"},
|
||||
{file = "ruff-0.3.1.tar.gz", hash = "sha256:d30db97141fc2134299e6e983a6727922c9e03c031ae4883a6d69461de722ae7"},
|
||||
{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.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"},
|
||||
{file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"},
|
||||
{file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"},
|
||||
{file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"},
|
||||
{file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3551,13 +3550,13 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "selenium"
|
||||
version = "4.18.1"
|
||||
version = "4.19.0"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "selenium-4.18.1-py3-none-any.whl", hash = "sha256:b24a3cdd2d47c29832e81345bfcde0c12bb608738013e53c781b211b418df241"},
|
||||
{file = "selenium-4.18.1.tar.gz", hash = "sha256:a11f67afa8bfac6b77e148c987b33f6b14eb1cae4d352722a75de1f26e3f0ae2"},
|
||||
{file = "selenium-4.19.0-py3-none-any.whl", hash = "sha256:5b4f49240d61e687a73f7968ae2517d403882aae3550eae2a229c745e619f1d9"},
|
||||
{file = "selenium-4.19.0.tar.gz", hash = "sha256:d9dfd6d0b021d71d0a48b865fe7746490ba82b81e9c87b212360006629eb1853"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3569,13 +3568,13 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "1.41.0"
|
||||
version = "1.44.0"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "sentry-sdk-1.41.0.tar.gz", hash = "sha256:4f2d6c43c07925d8cd10dfbd0970ea7cb784f70e79523cca9dbcd72df38e5a46"},
|
||||
{file = "sentry_sdk-1.41.0-py2.py3-none-any.whl", hash = "sha256:be4f8f4b29a80b6a3b71f0f31487beb9e296391da20af8504498a328befed53f"},
|
||||
{file = "sentry-sdk-1.44.0.tar.gz", hash = "sha256:f7125a9235795811962d52ff796dc032cd1d0dd98b59beaced8380371cd9c13c"},
|
||||
{file = "sentry_sdk-1.44.0-py2.py3-none-any.whl", hash = "sha256:eb65289da013ca92fad2694851ad2f086aa3825e808dc285bd7dcaf63602bb18"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3589,6 +3588,7 @@ asyncpg = ["asyncpg (>=0.23)"]
|
||||
beam = ["apache-beam (>=2.12)"]
|
||||
bottle = ["bottle (>=0.12.13)"]
|
||||
celery = ["celery (>=3)"]
|
||||
celery-redbeat = ["celery-redbeat (>=2)"]
|
||||
chalice = ["chalice (>=1.16.0)"]
|
||||
clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
|
||||
django = ["django (>=1.8)"]
|
||||
@ -3599,6 +3599,7 @@ grpcio = ["grpcio (>=1.21.1)"]
|
||||
httpx = ["httpx (>=0.16.0)"]
|
||||
huey = ["huey (>=2)"]
|
||||
loguru = ["loguru (>=0.5)"]
|
||||
openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
|
||||
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)"]
|
||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||
@ -3817,13 +3818,13 @@ wsproto = ">=0.14"
|
||||
|
||||
[[package]]
|
||||
name = "twilio"
|
||||
version = "9.0.0"
|
||||
version = "9.0.2"
|
||||
description = "Twilio API client and TwiML generator"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "twilio-9.0.0-py2.py3-none-any.whl", hash = "sha256:998bbda516e7257f5ab65b65012304b917e700688a39f3c72fb969612acf0879"},
|
||||
{file = "twilio-9.0.0.tar.gz", hash = "sha256:0b09919de65a982a0cd3b81db1b621f5fb8e4507f5cd9c35e5cf91128717e717"},
|
||||
{file = "twilio-9.0.2-py2.py3-none-any.whl", hash = "sha256:db89a8326f92240cdd8dc1dafb1d3f69169576243be9b9cbf5cf7778ecab0eed"},
|
||||
{file = "twilio-9.0.2.tar.gz", hash = "sha256:9450b7a9987c32146d1760c8680f92baa76b4ba570543758a4937da48ae46d77"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3961,48 +3962,33 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.18"
|
||||
version = "2.2.1"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"},
|
||||
{file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"},
|
||||
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
|
||||
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
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\""}
|
||||
pysocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""}
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
|
||||
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)"]
|
||||
|
||||
[[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"},
|
||||
]
|
||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
|
||||
h2 = ["h2 (>=4,<5)"]
|
||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.27.1"
|
||||
version = "0.29.0"
|
||||
description = "The lightning-fast ASGI server."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"},
|
||||
{file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"},
|
||||
{file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"},
|
||||
{file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -4215,13 +4201,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "webauthn"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
description = "Pythonic WebAuthn"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "webauthn-2.0.0-py3-none-any.whl", hash = "sha256:644dc68af5caaade06be6a2a2278775e85116e92dd755ad7a49d992d51c82033"},
|
||||
{file = "webauthn-2.0.0.tar.gz", hash = "sha256:12cc1759da98668b8242badc37c4129df300f89d89f5c183fac80e7b33c41dfd"},
|
||||
{file = "webauthn-2.1.0-py3-none-any.whl", hash = "sha256:9e1cf916e5ed7c01d54a6dfcc19dacbd2b87b81d2648f001b1fcbcb7aa2ff130"},
|
||||
{file = "webauthn-2.1.0.tar.gz", hash = "sha256:b196a4246c2818820857ba195c6e6e5398c761117f2269e3d2deab11c7995fc4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -4550,4 +4536,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "~3.12"
|
||||
content-hash = "825f1d552ba34206f7bfd55b70bfb42bc5d769605f59410703828ae787cd0baf"
|
||||
content-hash = "04ef13e2692c158e5eda1e89876a215746c56a891a161d8434d808dc12c0fc7a"
|
||||
|
@ -140,7 +140,7 @@ twilio = "*"
|
||||
twisted = "*"
|
||||
ua-parser = "*"
|
||||
# Pinned because of botocore https://github.com/orgs/python-poetry/discussions/7937
|
||||
urllib3 = { extras = ["secure"], version = "<2" }
|
||||
urllib3 = { extras = ["secure"], version = "<3" }
|
||||
uvicorn = { extras = ["standard"], version = "*" }
|
||||
watchdog = "*"
|
||||
webauthn = "*"
|
||||
|
493
schema.yml
493
schema.yml
@ -18512,6 +18512,7 @@ paths:
|
||||
- authentik_stages_password.passwordstage
|
||||
- authentik_stages_prompt.prompt
|
||||
- authentik_stages_prompt.promptstage
|
||||
- authentik_stages_source.sourcestage
|
||||
- authentik_stages_user_delete.userdeletestage
|
||||
- authentik_stages_user_login.userloginstage
|
||||
- authentik_stages_user_logout.userlogoutstage
|
||||
@ -18587,6 +18588,7 @@ paths:
|
||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||
* `authentik_stages_source.sourcestage` - Source Stage
|
||||
* `authentik_events.event` - Event
|
||||
* `authentik_events.notificationtransport` - Notification Transport
|
||||
* `authentik_events.notification` - Notification
|
||||
@ -18800,6 +18802,7 @@ paths:
|
||||
- authentik_stages_password.passwordstage
|
||||
- authentik_stages_prompt.prompt
|
||||
- authentik_stages_prompt.promptstage
|
||||
- authentik_stages_source.sourcestage
|
||||
- authentik_stages_user_delete.userdeletestage
|
||||
- authentik_stages_user_login.userloginstage
|
||||
- authentik_stages_user_logout.userlogoutstage
|
||||
@ -18875,6 +18878,7 @@ paths:
|
||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||
* `authentik_stages_source.sourcestage` - Source Stage
|
||||
* `authentik_events.event` - Event
|
||||
* `authentik_events.notificationtransport` - Notification Transport
|
||||
* `authentik_events.notification` - Notification
|
||||
@ -19922,6 +19926,10 @@ paths:
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: password_login_update_internal_password
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: peer_certificate
|
||||
schema:
|
||||
@ -27872,6 +27880,289 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
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/:
|
||||
get:
|
||||
operationId: stages_user_delete_list
|
||||
@ -29642,6 +29933,7 @@ components:
|
||||
- authentik.enterprise
|
||||
- authentik.enterprise.audit
|
||||
- authentik.enterprise.providers.rac
|
||||
- authentik.enterprise.stages.source
|
||||
- authentik.events
|
||||
type: string
|
||||
description: |-
|
||||
@ -29696,6 +29988,7 @@ components:
|
||||
* `authentik.enterprise` - authentik Enterprise
|
||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
||||
* `authentik.events` - authentik Events
|
||||
AppleChallengeResponseRequest:
|
||||
type: object
|
||||
@ -31493,8 +31786,7 @@ components:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
title: Pbm uuid
|
||||
title: Connection token uuid
|
||||
provider:
|
||||
type: integer
|
||||
provider_obj:
|
||||
@ -31504,7 +31796,6 @@ components:
|
||||
endpoint:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
endpoint_obj:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Endpoint'
|
||||
@ -31516,7 +31807,6 @@ components:
|
||||
required:
|
||||
- endpoint
|
||||
- endpoint_obj
|
||||
- pk
|
||||
- provider
|
||||
- provider_obj
|
||||
- user
|
||||
@ -31524,9 +31814,17 @@ components:
|
||||
type: object
|
||||
description: ConnectionToken Serializer
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
title: Connection token uuid
|
||||
provider:
|
||||
type: integer
|
||||
endpoint:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- endpoint
|
||||
- provider
|
||||
ConsentChallenge:
|
||||
type: object
|
||||
@ -32057,7 +32355,10 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
DummyChallengeResponseRequest:
|
||||
type: object
|
||||
@ -32739,6 +33040,7 @@ components:
|
||||
* `authentik.enterprise` - authentik Enterprise
|
||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
||||
* `authentik.events` - authentik Events
|
||||
model:
|
||||
allOf:
|
||||
@ -32816,6 +33118,7 @@ components:
|
||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||
* `authentik_stages_source.sourcestage` - Source Stage
|
||||
* `authentik_events.event` - Event
|
||||
* `authentik_events.notificationtransport` - Notification Transport
|
||||
* `authentik_events.notification` - Notification
|
||||
@ -32938,6 +33241,7 @@ components:
|
||||
* `authentik.enterprise` - authentik Enterprise
|
||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
||||
* `authentik.events` - authentik Events
|
||||
model:
|
||||
allOf:
|
||||
@ -33015,6 +33319,7 @@ components:
|
||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||
* `authentik_stages_source.sourcestage` - Source Stage
|
||||
* `authentik_events.event` - Event
|
||||
* `authentik_events.notificationtransport` - Notification Transport
|
||||
* `authentik_events.notification` - Notification
|
||||
@ -33477,8 +33782,7 @@ components:
|
||||
logs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
$ref: '#/components/schemas/LogEvent'
|
||||
readOnly: true
|
||||
success:
|
||||
type: boolean
|
||||
@ -34036,6 +34340,8 @@ components:
|
||||
type: boolean
|
||||
application_pre:
|
||||
type: string
|
||||
flow_designation:
|
||||
$ref: '#/components/schemas/FlowDesignationEnum'
|
||||
enroll_url:
|
||||
type: string
|
||||
recovery_url:
|
||||
@ -34051,6 +34357,7 @@ components:
|
||||
show_source_labels:
|
||||
type: boolean
|
||||
required:
|
||||
- flow_designation
|
||||
- password_fields
|
||||
- primary_action
|
||||
- show_source_labels
|
||||
@ -34921,6 +35228,10 @@ components:
|
||||
object_uniqueness_field:
|
||||
type: string
|
||||
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:
|
||||
type: boolean
|
||||
sync_users_password:
|
||||
@ -35062,6 +35373,10 @@ components:
|
||||
type: string
|
||||
minLength: 1
|
||||
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:
|
||||
type: boolean
|
||||
sync_users_password:
|
||||
@ -35199,6 +35514,48 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- 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:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/RedirectChallenge'
|
||||
@ -35330,6 +35687,7 @@ components:
|
||||
- authentik_providers_rac.racprovider
|
||||
- authentik_providers_rac.endpoint
|
||||
- authentik_providers_rac.racpropertymapping
|
||||
- authentik_stages_source.sourcestage
|
||||
- authentik_events.event
|
||||
- authentik_events.notificationtransport
|
||||
- authentik_events.notification
|
||||
@ -35406,6 +35764,7 @@ components:
|
||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||
* `authentik_stages_source.sourcestage` - Source Stage
|
||||
* `authentik_events.event` - Event
|
||||
* `authentik_events.notificationtransport` - Notification Transport
|
||||
* `authentik_events.notification` - Notification
|
||||
@ -37340,6 +37699,18 @@ components:
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedSourceStageList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SourceStage'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedStageList:
|
||||
type: object
|
||||
properties:
|
||||
@ -38276,8 +38647,15 @@ components:
|
||||
type: object
|
||||
description: ConnectionToken Serializer
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
title: Connection token uuid
|
||||
provider:
|
||||
type: integer
|
||||
endpoint:
|
||||
type: string
|
||||
format: uuid
|
||||
PatchedConsentStageRequest:
|
||||
type: object
|
||||
description: ConsentStage Serializer
|
||||
@ -38583,6 +38961,7 @@ components:
|
||||
* `authentik.enterprise` - authentik Enterprise
|
||||
* `authentik.enterprise.audit` - authentik Enterprise.Audit
|
||||
* `authentik.enterprise.providers.rac` - authentik Enterprise.Providers.RAC
|
||||
* `authentik.enterprise.stages.source` - authentik Enterprise.Stages.Source
|
||||
* `authentik.events` - authentik Events
|
||||
model:
|
||||
allOf:
|
||||
@ -38660,6 +39039,7 @@ components:
|
||||
* `authentik_providers_rac.racprovider` - RAC Provider
|
||||
* `authentik_providers_rac.endpoint` - RAC Endpoint
|
||||
* `authentik_providers_rac.racpropertymapping` - RAC Property Mapping
|
||||
* `authentik_stages_source.sourcestage` - Source Stage
|
||||
* `authentik_events.event` - Event
|
||||
* `authentik_events.notificationtransport` - Notification Transport
|
||||
* `authentik_events.notification` - Notification
|
||||
@ -39113,6 +39493,10 @@ components:
|
||||
type: string
|
||||
minLength: 1
|
||||
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:
|
||||
type: boolean
|
||||
sync_users_password:
|
||||
@ -40242,6 +40626,25 @@ components:
|
||||
impersonation:
|
||||
type: boolean
|
||||
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:
|
||||
type: object
|
||||
description: Serializer for static authenticator devices
|
||||
@ -40947,8 +41350,7 @@ components:
|
||||
log_messages:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
$ref: '#/components/schemas/LogEvent'
|
||||
readOnly: true
|
||||
required:
|
||||
- log_messages
|
||||
@ -43586,6 +43988,74 @@ components:
|
||||
required:
|
||||
- name
|
||||
- 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:
|
||||
type: object
|
||||
description: Serializer for SourceType
|
||||
@ -43894,7 +44364,7 @@ components:
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
$ref: '#/components/schemas/LogEvent'
|
||||
required:
|
||||
- description
|
||||
- duration
|
||||
@ -45184,6 +45654,10 @@ components:
|
||||
type: string
|
||||
description: Get latest version from cache
|
||||
readOnly: true
|
||||
version_latest_valid:
|
||||
type: boolean
|
||||
description: Check if latest version is valid
|
||||
readOnly: true
|
||||
build_hash:
|
||||
type: string
|
||||
description: Get build hash, if version is not latest or released
|
||||
@ -45197,6 +45671,7 @@ components:
|
||||
- outdated
|
||||
- version_current
|
||||
- version_latest
|
||||
- version_latest_valid
|
||||
WebAuthnDevice:
|
||||
type: object
|
||||
description: Serializer for WebAuthn authenticator devices
|
||||
|
@ -128,6 +128,7 @@ class TestSourceLDAPSamba(SeleniumTestCase):
|
||||
base_dn="dc=test,dc=goauthentik,dc=io",
|
||||
additional_user_dn="ou=users",
|
||||
additional_group_dn="ou=groups",
|
||||
password_login_update_internal_password=True,
|
||||
)
|
||||
source.property_mappings.set(
|
||||
LDAPPropertyMapping.objects.filter(
|
||||
|
@ -10,6 +10,7 @@ from kubernetes.client.exceptions import OpenApiException
|
||||
from authentik.core.tests.utils import create_test_flow
|
||||
from authentik.lib.config import CONFIG
|
||||
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.models import KubernetesServiceConnection, Outpost, OutpostType
|
||||
from authentik.outposts.tasks import outpost_connection_discovery
|
||||
@ -91,6 +92,35 @@ class OutpostKubernetesTests(TestCase):
|
||||
|
||||
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)
|
||||
def test_controller_rename(self):
|
||||
"""test that objects get deleted and re-created with new names"""
|
||||
|
@ -26,12 +26,6 @@ class TestProxyKubernetes(TestCase):
|
||||
outpost_connection_discovery()
|
||||
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)
|
||||
def test_kubernetes_controller_static(self):
|
||||
"""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",
|
||||
"dependencies": {
|
||||
"chromedriver": "^122.0.4"
|
||||
"chromedriver": "^123.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
"@wdio/cli": "^8.33.0",
|
||||
"@wdio/local-runner": "^8.33.0",
|
||||
"@wdio/mocha-framework": "^8.33.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||
"@typescript-eslint/parser": "^7.4.0",
|
||||
"@wdio/cli": "^8.35.1",
|
||||
"@wdio/local-runner": "^8.35.1",
|
||||
"@wdio/mocha-framework": "^8.35.0",
|
||||
"@wdio/spec-reporter": "^8.32.4",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-sonarjs": "^0.24.0",
|
||||
"eslint-plugin-sonarjs": "^0.25.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.2.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.2",
|
||||
"typescript": "^5.4.3",
|
||||
"wdio-wait-for": "^3.0.11"
|
||||
},
|
||||
"engines": {
|
||||
@ -889,10 +889,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.0.tgz",
|
||||
"integrity": "sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==",
|
||||
"devOptional": true
|
||||
"version": "20.11.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
|
||||
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.4",
|
||||
@ -952,16 +955,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz",
|
||||
"integrity": "sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz",
|
||||
"integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "7.1.1",
|
||||
"@typescript-eslint/type-utils": "7.1.1",
|
||||
"@typescript-eslint/utils": "7.1.1",
|
||||
"@typescript-eslint/visitor-keys": "7.1.1",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/type-utils": "7.4.0",
|
||||
"@typescript-eslint/utils": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.4",
|
||||
@ -970,7 +973,7 @@
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -987,19 +990,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.1.tgz",
|
||||
"integrity": "sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz",
|
||||
"integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "7.1.1",
|
||||
"@typescript-eslint/types": "7.1.1",
|
||||
"@typescript-eslint/typescript-estree": "7.1.1",
|
||||
"@typescript-eslint/visitor-keys": "7.1.1",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -1015,16 +1018,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz",
|
||||
"integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz",
|
||||
"integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.1.1",
|
||||
"@typescript-eslint/visitor-keys": "7.1.1"
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -1032,18 +1035,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.1.tgz",
|
||||
"integrity": "sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz",
|
||||
"integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "7.1.1",
|
||||
"@typescript-eslint/utils": "7.1.1",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"@typescript-eslint/utils": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -1059,12 +1062,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz",
|
||||
"integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz",
|
||||
"integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -1072,13 +1075,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz",
|
||||
"integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz",
|
||||
"integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.1.1",
|
||||
"@typescript-eslint/visitor-keys": "7.1.1",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -1087,7 +1090,7 @@
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -1124,21 +1127,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.1.tgz",
|
||||
"integrity": "sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz",
|
||||
"integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "7.1.1",
|
||||
"@typescript-eslint/types": "7.1.1",
|
||||
"@typescript-eslint/typescript-estree": "7.1.1",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -1149,16 +1152,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz",
|
||||
"integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz",
|
||||
"integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.1.1",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -1186,19 +1189,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/cli": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.33.0.tgz",
|
||||
"integrity": "sha512-/DwH6uF9c5zF5WOrImpaONGKdkbeGii8Cei9AYp1BZmBH6jqevSCZI8LhTMS+qRkUei1AlukSWE8QRpat03yQQ==",
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.35.1.tgz",
|
||||
"integrity": "sha512-cdFmd6P/eQJdP2lChQ+Fa9b1c2p0bDIPmetVHGCuHiW8ZPkanrvBFtHMUhMu44a1koni9LvN/hu7vIJ/aAC+Rg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.1",
|
||||
"@vitest/snapshot": "^1.2.1",
|
||||
"@wdio/config": "8.33.0",
|
||||
"@wdio/globals": "8.33.0",
|
||||
"@wdio/config": "8.35.0",
|
||||
"@wdio/globals": "8.35.1",
|
||||
"@wdio/logger": "8.28.0",
|
||||
"@wdio/protocols": "8.32.0",
|
||||
"@wdio/types": "8.32.4",
|
||||
"@wdio/utils": "8.33.0",
|
||||
"@wdio/utils": "8.35.0",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"chalk": "^5.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
@ -1213,7 +1216,7 @@
|
||||
"lodash.union": "^4.6.0",
|
||||
"read-pkg-up": "10.0.0",
|
||||
"recursive-readdir": "^2.2.3",
|
||||
"webdriverio": "8.33.0",
|
||||
"webdriverio": "8.35.1",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
@ -1236,14 +1239,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/config": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.33.0.tgz",
|
||||
"integrity": "sha512-L2C8QK0cG645mviTGhjl1uSmnnIEs+kmUGDNNijLu1PqxK0YP5RGL3SSr3zTNRyp0CTib7P31ekriWYqURfCsw==",
|
||||
"version": "8.35.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.35.0.tgz",
|
||||
"integrity": "sha512-I36sBPMl/+LCyQ3Pwb8gGQM6KxwmUfhOPp16TxN21Qo/Bc0fZfyGIg6KevmRu4DuqpGUm5MMVSfyPhLUkMk3Cg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@wdio/logger": "8.28.0",
|
||||
"@wdio/types": "8.32.4",
|
||||
"@wdio/utils": "8.33.0",
|
||||
"@wdio/utils": "8.35.0",
|
||||
"decamelize": "^6.0.0",
|
||||
"deepmerge-ts": "^5.0.0",
|
||||
"glob": "^10.2.2",
|
||||
@ -1254,28 +1257,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/globals": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.33.0.tgz",
|
||||
"integrity": "sha512-EeVaMeQhLkicSY48d//wAdZP9S5LsFpzwUF9njjtq5sybaCoVLWejb26/izb0L81FnDJuMAk1XGDhjCTcr3VMg==",
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.35.1.tgz",
|
||||
"integrity": "sha512-T3IUFcKXRU9WWleAV72DGFWUiXSSr8SBvpc2cUJrvZ5Je9R2gEsrts5eHCY7amXtfeylfMgy5EayGMajgcna6A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.13 || >=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"expect-webdriverio": "^4.11.2",
|
||||
"webdriverio": "8.33.0"
|
||||
"webdriverio": "8.35.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/local-runner": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.33.0.tgz",
|
||||
"integrity": "sha512-Acta99MyLpgdl39alujYeQx4DgwcRctxaTGNzekxkCNZYFMIjEX3E8brnRUhsexVvGLwWvW/wAAHLQZsqf4SSw==",
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.35.1.tgz",
|
||||
"integrity": "sha512-PG+bADoY5VoWPmAfRi030rtxbFj68MVPlcwEN0dN1lDdYKz1ATzzGUK12sqCgGz1ktcC7sQzmJZVBklzbvn3mQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/logger": "8.28.0",
|
||||
"@wdio/repl": "8.24.12",
|
||||
"@wdio/runner": "8.33.0",
|
||||
"@wdio/runner": "8.35.1",
|
||||
"@wdio/types": "8.32.4",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"split2": "^4.1.0",
|
||||
@ -1313,16 +1316,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/mocha-framework": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.33.0.tgz",
|
||||
"integrity": "sha512-40N+Im4mXKp4tIAzVaOl+sbSy9+JKUZbt7KicranFw6RoB7XlreUUidzU7pNO04fm51t6Mb4O2x4kEdfesF+Jw==",
|
||||
"version": "8.35.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.35.0.tgz",
|
||||
"integrity": "sha512-riO3aMgvGdFFRMpyMk5m480V+mi5EcKk6cjT1TB9L5XEN7Mo/8qthBw9CLgFCZkr4KlR40hgPKSZFHE0rH2GpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/logger": "8.28.0",
|
||||
"@wdio/types": "8.32.4",
|
||||
"@wdio/utils": "8.33.0",
|
||||
"@wdio/utils": "8.35.0",
|
||||
"mocha": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -1364,22 +1367,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/runner": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.33.0.tgz",
|
||||
"integrity": "sha512-NZscR+Yc13Gugi2EHU+LH+r92Wo0Y+kKaGH1luufrXN/DUzrIaxGDsJxv2czC0FBx/FIwrkHbg53AE8KEt3e7g==",
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.35.1.tgz",
|
||||
"integrity": "sha512-5F6cbOYeZjF34Vsnycp5JPnDljI52fmyxsV2O/L3h6F2+83YXpbsqBplw/2G24JtIUudV7VOY/38bUicn1OyXg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/config": "8.33.0",
|
||||
"@wdio/globals": "8.33.0",
|
||||
"@types/node": "^20.11.28",
|
||||
"@wdio/config": "8.35.0",
|
||||
"@wdio/globals": "8.35.1",
|
||||
"@wdio/logger": "8.28.0",
|
||||
"@wdio/types": "8.32.4",
|
||||
"@wdio/utils": "8.33.0",
|
||||
"deepmerge-ts": "^5.0.0",
|
||||
"expect-webdriverio": "^4.11.2",
|
||||
"gaze": "^1.1.2",
|
||||
"webdriver": "8.33.0",
|
||||
"webdriverio": "8.33.0"
|
||||
"@wdio/utils": "8.35.0",
|
||||
"deepmerge-ts": "^5.1.0",
|
||||
"expect-webdriverio": "^4.12.0",
|
||||
"gaze": "^1.1.3",
|
||||
"webdriver": "8.35.0",
|
||||
"webdriverio": "8.35.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13 || >=18"
|
||||
@ -1426,9 +1429,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@wdio/utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.33.0.tgz",
|
||||
"integrity": "sha512-XdNIZXTPF6Y89C/80GVexvz5p5a1NCaN/i2bw58PeDOlPYnvD5w3VIQZg0bi4n8lsPwJGseO0/y9qAGYkr8WwQ==",
|
||||
"version": "8.35.0",
|
||||
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.35.0.tgz",
|
||||
"integrity": "sha512-9KCyn4aS+9tWfthnUkNFVe52AM6QrLGAeIxgGxNlzTAcQGl7jjwdDM7aSK0RjLkWI3a/88DRH21mN/t2LGDmPQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@puppeteer/browsers": "^1.6.0",
|
||||
@ -2081,9 +2084,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chromedriver": {
|
||||
"version": "122.0.4",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-122.0.4.tgz",
|
||||
"integrity": "sha512-MxkaWaxCqefHyh9UorGzl1F6ZNBgC7pqgT0piAysLZdw20ojSgJ62ljG8SFbhDJqBTegKbmuioa6MQ1m4Czdsg==",
|
||||
"version": "123.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.1.tgz",
|
||||
"integrity": "sha512-YQUIP/zdlzDIRCZNCv6rEVDSY4RAxo/tDL0OiGPPuai+z8unRNqJr/9V6XTBypVFyDheXNalKt9QxEqdMPuLAQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@testim/chrome-version": "^1.1.4",
|
||||
@ -2688,9 +2691,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/devtools-protocol": {
|
||||
"version": "0.0.1263784",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1263784.tgz",
|
||||
"integrity": "sha512-k0SCZMwj587w4F8QYbP5iIbSonL6sd3q8aVJch036r9Tv2t9b5/Oq7AiJ/FJvRuORm/pJNXZtrdNNWlpRnl56A==",
|
||||
"version": "0.0.1273771",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1273771.tgz",
|
||||
"integrity": "sha512-QDbb27xcTVReQQW/GHJsdQqGKwYBE7re7gxehj467kKP2DKuYBUj6i2k5LRiAC66J1yZG/9gsxooz/s9pcm0Og==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/diff": {
|
||||
@ -3111,9 +3114,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-sonarjs": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.24.0.tgz",
|
||||
"integrity": "sha512-87zp50mbbNrSTuoEOebdRQBPa0mdejA5UEjyuScyIw8hEpEjfWP89Qhkq5xVZfVyVSRQKZc9alVm7yRKQvvUmg==",
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.0.tgz",
|
||||
"integrity": "sha512-DaZOtpUucEZbvowgKxVFwICV6r0h7jSCAx0IHICvCowP+etFussnhtaiCPSnYAuwVJ+P/6UFUhkv7QJklpXFyA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
@ -3410,9 +3413,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/expect-webdriverio": {
|
||||
"version": "4.11.2",
|
||||
"resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.11.2.tgz",
|
||||
"integrity": "sha512-PK8lrQmRhK8NRtE8i/CJsnKS/QGrHULQW5EfmyKxIiLHnXd7t8dX0dWJn/fiYVtrPKBUyXSL6h52QqvZVc9yGQ==",
|
||||
"version": "4.12.1",
|
||||
"resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.12.1.tgz",
|
||||
"integrity": "sha512-jTfyC2bJbPNw4c8MlEwZNX7SjtPbZ73ysJvr/OGKA9mSKC+toyjU2eMNzHlt9WZO5+wl0RDS1dR7VxHXeu7+zA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vitest/snapshot": "^1.2.2",
|
||||
@ -3696,9 +3699,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@ -8617,9 +8620,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
|
||||
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
|
||||
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@ -8654,6 +8657,12 @@
|
||||
"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": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
@ -8877,18 +8886,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.33.0.tgz",
|
||||
"integrity": "sha512-00iTlsKYM1zWhkabQKqI3U0NpiZ8H81SCmRDoReP4Z13aHAesWVMW3qJu2tZgEHlj5DVCxt2aV/eTE1nUUEZRQ==",
|
||||
"version": "8.35.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.35.0.tgz",
|
||||
"integrity": "sha512-D13EroddIXDqdq3jgO8j6sorgTWqTwEiTqwlDoJizpRIgHGBy+UjkNM7XW1yVcvt8gsD2Dei2LQth2tJEnu5Ng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@wdio/config": "8.33.0",
|
||||
"@wdio/config": "8.35.0",
|
||||
"@wdio/logger": "8.28.0",
|
||||
"@wdio/protocols": "8.32.0",
|
||||
"@wdio/types": "8.32.4",
|
||||
"@wdio/utils": "8.33.0",
|
||||
"@wdio/utils": "8.35.0",
|
||||
"deepmerge-ts": "^5.1.0",
|
||||
"got": "^12.6.1",
|
||||
"ky": "^0.33.0",
|
||||
@ -8899,23 +8908,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriverio": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.33.0.tgz",
|
||||
"integrity": "sha512-i7Ey+pvQOOlwLgLOI/5Kohoys1V3o8QZwM2/UtOW8q9MFcq4trUlgHrnEcRk79diWKmMjc/DO55PdZ9LitykcA==",
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.35.1.tgz",
|
||||
"integrity": "sha512-YAuKR4JERGiMqCJmm5fEVZ160iiFPyupwALqfXfzrYVcEmKltKPFY/oUCArmi6Uzqd+Sa2Kp9WZtz2Eu1R76JA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.1.0",
|
||||
"@wdio/config": "8.33.0",
|
||||
"@wdio/config": "8.35.0",
|
||||
"@wdio/logger": "8.28.0",
|
||||
"@wdio/protocols": "8.32.0",
|
||||
"@wdio/repl": "8.24.12",
|
||||
"@wdio/types": "8.32.4",
|
||||
"@wdio/utils": "8.33.0",
|
||||
"@wdio/utils": "8.35.0",
|
||||
"archiver": "^7.0.0",
|
||||
"aria-query": "^5.0.0",
|
||||
"css-shorthand-properties": "^1.1.1",
|
||||
"css-value": "^0.0.1",
|
||||
"devtools-protocol": "^0.0.1263784",
|
||||
"devtools-protocol": "^0.0.1273771",
|
||||
"grapheme-splitter": "^1.0.2",
|
||||
"import-meta-resolve": "^4.0.0",
|
||||
"is-plain-obj": "^4.1.0",
|
||||
@ -8927,7 +8936,7 @@
|
||||
"resq": "^1.9.1",
|
||||
"rgb2hex": "0.2.5",
|
||||
"serialize-error": "^11.0.1",
|
||||
"webdriver": "8.33.0"
|
||||
"webdriver": "8.35.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13 || >=18"
|
||||
|
@ -4,19 +4,19 @@
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
"@wdio/cli": "^8.33.0",
|
||||
"@wdio/local-runner": "^8.33.0",
|
||||
"@wdio/mocha-framework": "^8.33.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||
"@typescript-eslint/parser": "^7.4.0",
|
||||
"@wdio/cli": "^8.35.1",
|
||||
"@wdio/local-runner": "^8.35.1",
|
||||
"@wdio/mocha-framework": "^8.35.0",
|
||||
"@wdio/spec-reporter": "^8.32.4",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-sonarjs": "^0.24.0",
|
||||
"eslint-plugin-sonarjs": "^0.25.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.2.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.2",
|
||||
"typescript": "^5.4.3",
|
||||
"wdio-wait-for": "^3.0.11"
|
||||
},
|
||||
"scripts": {
|
||||
@ -32,6 +32,6 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"dependencies": {
|
||||
"chromedriver": "^122.0.4"
|
||||
"chromedriver": "^123.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
// options for building: watching, building, and building the proxy.
|
||||
|
||||
// Ordered by largest to smallest interface to build even faster
|
||||
const interfaces = [
|
||||
["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"],
|
||||
["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", "."],
|
||||
];
|
||||
|
||||
const baseArgs = {
|
||||
@ -80,29 +80,32 @@ const baseArgs = {
|
||||
format: "esm",
|
||||
};
|
||||
|
||||
function buildAuthentik(interfaces) {
|
||||
for (const [source, dest] of interfaces) {
|
||||
const DIST = path.join(__dirname, "./dist", dest);
|
||||
console.log(`[${new Date(Date.now()).toISOString()}] Starting build for target ${source}`);
|
||||
try {
|
||||
const start = Date.now();
|
||||
esbuild.buildSync({
|
||||
...baseArgs,
|
||||
entryPoints: [`./src/${source}`],
|
||||
outdir: DIST,
|
||||
});
|
||||
const end = Date.now();
|
||||
console.log(
|
||||
`[${new Date(end).toISOString()}] Finished build for target ${source} in ${Date.now() - start}ms`,
|
||||
);
|
||||
} catch (exc) {
|
||||
console.error(
|
||||
`[${new Date(Date.now()).toISOString()}] Failed to build ${source}: ${exc}`,
|
||||
);
|
||||
}
|
||||
async function buildOneSource(source, dest) {
|
||||
const DIST = path.join(__dirname, "./dist", dest);
|
||||
console.log(`[${new Date(Date.now()).toISOString()}] Starting build for target ${source}`);
|
||||
|
||||
try {
|
||||
const start = Date.now();
|
||||
await esbuild.build({
|
||||
...baseArgs,
|
||||
entryPoints: [`./src/${source}`],
|
||||
outdir: DIST,
|
||||
});
|
||||
const end = Date.now();
|
||||
console.log(
|
||||
`[${new Date(end).toISOString()}] Finished build for target ${source} in ${
|
||||
Date.now() - start
|
||||
}ms`,
|
||||
);
|
||||
} catch (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;
|
||||
function debouncedBuild() {
|
||||
if (timeoutId !== null) {
|
||||
@ -138,9 +141,11 @@ 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")) {
|
||||
// There's no watch-for-proxy, sorry.
|
||||
buildAuthentik(interfaces.slice(0, 2));
|
||||
await buildAuthentik(
|
||||
interfaces.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)),
|
||||
);
|
||||
process.exit(0);
|
||||
} else {
|
||||
// And the fallback: just build it.
|
||||
buildAuthentik(interfaces);
|
||||
await buildAuthentik(interfaces);
|
||||
}
|
||||
|
17
web/docs/Changelog.md
Normal file
17
web/docs/Changelog.md
Normal file
@ -0,0 +1,17 @@
|
||||
### 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
|
2438
web/package-lock.json
generated
2438
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-proxy": "run-s build-locales esbuild:build-proxy",
|
||||
"watch": "run-s build-locales esbuild:watch",
|
||||
"lint": "eslint . --max-warnings 0 --fix",
|
||||
"lint:precommit": "node scripts/eslint-precommit.mjs",
|
||||
"lint": "cross-env NODE_OPTIONS='--max_old_space_size=8192' eslint . --max-warnings 0 --fix",
|
||||
"lint:precommit": "cross-env NODE_OPTIONS='--max_old_space_size=8192' node scripts/eslint-precommit.mjs",
|
||||
"lint:spelling": "node scripts/check-spelling.mjs",
|
||||
"lit-analyse": "lit-analyzer src",
|
||||
"precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
|
||||
@ -32,31 +32,32 @@
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "^6.4.8",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.4",
|
||||
"@codemirror/lang-python": "^6.1.5",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.3.3",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@formatjs/intl-listformat": "^7.5.5",
|
||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||
"@goauthentik/api": "^2024.2.2-1709583949",
|
||||
"@lit-labs/context": "^0.4.0",
|
||||
"@goauthentik/api": "^2024.2.2-1711643691",
|
||||
"@lit-labs/task": "^3.1.0",
|
||||
"@lit/localize": "^0.11.4",
|
||||
"@lit/context": "^1.1.0",
|
||||
"@lit/localize": "^0.12.1",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^2.4.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^7.106.0",
|
||||
"@sentry/browser": "^7.109.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.4.2",
|
||||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.36.0",
|
||||
"country-flag-icons": "^1.5.9",
|
||||
"core-js": "^3.36.1",
|
||||
"country-flag-icons": "^1.5.10",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"lit": "^2.8.0",
|
||||
"lit": "^3.1.2",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^10.9.0",
|
||||
"rapidoc": "^9.3.4",
|
||||
@ -67,18 +68,19 @@
|
||||
"yaml": "^2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.0",
|
||||
"@babel/core": "^7.24.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.0",
|
||||
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.23.4",
|
||||
"@babel/plugin-transform-runtime": "^7.24.0",
|
||||
"@babel/preset-env": "^7.24.0",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.1",
|
||||
"@babel/plugin-transform-private-methods": "^7.24.1",
|
||||
"@babel/plugin-transform-private-property-in-object": "^7.24.1",
|
||||
"@babel/plugin-transform-runtime": "^7.24.3",
|
||||
"@babel/preset-env": "^7.24.3",
|
||||
"@babel/preset-typescript": "^7.24.1",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
|
||||
"@lit/localize-tools": "^0.7.2",
|
||||
"@spotlightjs/spotlight": "^1.2.12",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@spotlightjs/spotlight": "^1.2.16",
|
||||
"@storybook/addon-essentials": "^7.6.17",
|
||||
"@storybook/addon-links": "^7.6.17",
|
||||
"@storybook/api": "^7.6.17",
|
||||
@ -89,48 +91,47 @@
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/chart.js": "^2.9.41",
|
||||
"@types/codemirror": "5.60.15",
|
||||
"@types/grecaptcha": "^3.0.8",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "1.5.2",
|
||||
"@types/showdown": "^2.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||
"@typescript-eslint/parser": "^7.4.0",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
"chokidar": "^3.6.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"esbuild": "^0.20.1",
|
||||
"esbuild": "^0.20.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-custom-elements": "0.0.8",
|
||||
"eslint-plugin-lit": "^1.11.0",
|
||||
"eslint-plugin-sonarjs": "^0.24.0",
|
||||
"eslint-plugin-sonarjs": "^0.25.0",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"github-slugger": "^2.0.0",
|
||||
"glob": "^10.3.10",
|
||||
"glob": "^10.3.12",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.2.5",
|
||||
"pseudolocale": "^2.0.0",
|
||||
"react": "^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-addon-mock": "^4.3.0",
|
||||
"ts-lit-plugin": "^2.0.2",
|
||||
"tslib": "^2.6.2",
|
||||
"turnstile-types": "^1.2.0",
|
||||
"typescript": "^5.4.2",
|
||||
"vite-tsconfig-paths": "^4.3.1"
|
||||
"typescript": "^5.4.3",
|
||||
"vite-tsconfig-paths": "^4.3.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.20.1",
|
||||
"@esbuild/linux-amd64": "^0.18.11",
|
||||
"@esbuild/linux-arm64": "^0.20.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.12.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.12.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.12.1"
|
||||
"@rollup/rollup-darwin-arm64": "4.13.2",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.13.2",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.13.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { PFSize } from "@goauthentik/elements/Spinner";
|
||||
import { PFSize } from "@goauthentik/common/enums.js";
|
||||
import { AggregateCard } from "@goauthentik/elements/cards/AggregateCard";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
|
||||
import { getRelativeTime } from "@goauthentik/app/common/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EventWithContext } from "@goauthentik/common/events";
|
||||
import { actionToLabel } from "@goauthentik/common/labels";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-event-info";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
import "@goauthentik/elements/buttons/Dropdown";
|
||||
|
@ -31,9 +31,15 @@ export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
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>({
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
message: html`${msg("Up-to-date!")}`,
|
||||
icon: "fa fa-question-circle",
|
||||
message: html`${msg("Latest version unknown")}`,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { first } from "@goauthentik/app/common/utils";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/elements/events/LogViewer";
|
||||
import { Form } from "@goauthentik/elements/forms/Form";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
@ -83,28 +84,7 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
${(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>`}
|
||||
<ak-log-viewer .logs=${this.result?.logMessages}></ak-log-viewer>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "@goauthentik/admin/applications/ApplicationForm";
|
||||
import { PFSize } from "@goauthentik/app/elements/Spinner";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PFSize } from "@goauthentik/common/enums.js";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/components/ak-app-icon";
|
||||
import MDApplication from "@goauthentik/docs/applications/index.md";
|
||||
|
@ -2,9 +2,8 @@ import "@goauthentik/admin/applications/ApplicationAuthorizeChart";
|
||||
import "@goauthentik/admin/applications/ApplicationCheckAccessForm";
|
||||
import "@goauthentik/admin/applications/ApplicationForm";
|
||||
import "@goauthentik/admin/policies/BoundPoliciesList";
|
||||
import { PFSize } from "@goauthentik/app/elements/Spinner";
|
||||
import "@goauthentik/app/elements/rbac/ObjectPermissionsPage";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PFSize } from "@goauthentik/common/enums.js";
|
||||
import "@goauthentik/components/ak-app-icon";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
@ -12,9 +11,10 @@ import "@goauthentik/elements/EmptyState";
|
||||
import "@goauthentik/elements/PageHeader";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/rbac/ObjectPermissionsPage";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@ -37,37 +37,11 @@ import {
|
||||
|
||||
@customElement("ak-application-view")
|
||||
export class ApplicationViewPage extends AKElement {
|
||||
@property()
|
||||
set applicationSlug(value: string) {
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreApplicationsRetrieve({
|
||||
slug: value,
|
||||
})
|
||||
.then((app) => {
|
||||
this.application = app;
|
||||
if (
|
||||
app.providerObj &&
|
||||
[
|
||||
"authentik_providers_proxy.proxyprovider",
|
||||
"authentik_providers_ldap.ldapprovider",
|
||||
].includes(app.providerObj.metaModelName)
|
||||
) {
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsInstancesList({
|
||||
providersByPk: [app.provider || 0],
|
||||
pageSize: 1,
|
||||
})
|
||||
.then((outposts) => {
|
||||
if (outposts.pagination.count < 1) {
|
||||
this.missingOutpost = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@property({ type: String })
|
||||
applicationSlug?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
application!: Application;
|
||||
@state()
|
||||
application?: Application;
|
||||
|
||||
@state()
|
||||
missingOutpost = false;
|
||||
@ -86,6 +60,40 @@ export class ApplicationViewPage extends AKElement {
|
||||
];
|
||||
}
|
||||
|
||||
fetchIsMissingOutpost(providersByPk: Array<number>) {
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsInstancesList({
|
||||
providersByPk,
|
||||
pageSize: 1,
|
||||
})
|
||||
.then((outposts) => {
|
||||
if (outposts.pagination.count < 1) {
|
||||
this.missingOutpost = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchApplication(slug: string) {
|
||||
new CoreApi(DEFAULT_CONFIG).coreApplicationsRetrieve({ slug }).then((app) => {
|
||||
this.application = app;
|
||||
if (
|
||||
app.providerObj &&
|
||||
[
|
||||
RbacPermissionsAssignedByUsersListModelEnum.ProvidersProxyProxyprovider.toString(),
|
||||
RbacPermissionsAssignedByUsersListModelEnum.ProvidersLdapLdapprovider.toString(),
|
||||
].includes(app.providerObj.metaModelName)
|
||||
) {
|
||||
this.fetchIsMissingOutpost([app.provider || 0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("applicationSlug") && this.applicationSlug) {
|
||||
this.fetchApplication(this.applicationSlug);
|
||||
}
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
header=${this.application?.name || msg("Loading")}
|
||||
|
@ -20,8 +20,9 @@ export class AkBackchannelProvidersInput extends AKElement {
|
||||
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
||||
// general.
|
||||
|
||||
protected createRenderRoot() {
|
||||
return this;
|
||||
return this as HTMLElement;
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
|
@ -4,7 +4,7 @@ import { KeyUnknown, serializeForm } from "@goauthentik/elements/forms/Form";
|
||||
import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||
|
||||
import { consume } from "@lit-labs/context";
|
||||
import { consume } from "@lit/context";
|
||||
import { query } from "@lit/reactive-element/decorators.js";
|
||||
|
||||
import { styles as AwadStyles } from "./BasePanel.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createContext } from "@lit-labs/context";
|
||||
import { createContext } from "@lit/context";
|
||||
|
||||
import { ApplicationWizardState } from "./types";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AkWizard } from "@goauthentik/components/ak-wizard-main/AkWizard";
|
||||
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||
|
||||
import { ContextProvider } from "@lit-labs/context";
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { WithLicenseSummary } from "@goauthentik/app/elements/Interface/licenseSummaryProvider";
|
||||
import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user