Merge branch 'master' into inbuilt-proxy
This commit is contained in:
commit
8429dd19b2
|
@ -76,11 +76,11 @@
|
||||||
},
|
},
|
||||||
"asgiref": {
|
"asgiref": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee",
|
"sha256:05914d0fa65a21711e732adc6572edad6c8da5f1435c3f0c060689ced5e85195",
|
||||||
"sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"
|
"sha256:d36fa91dd90e3aa3c81a6bd426ccc8fb20bd3d22b0cf14a12800289e9c3e2563"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.6'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==3.3.4"
|
"version": "==3.4.0"
|
||||||
},
|
},
|
||||||
"async-timeout": {
|
"async-timeout": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -122,19 +122,19 @@
|
||||||
},
|
},
|
||||||
"boto3": {
|
"boto3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2c2f70608934b03f9c08f4cd185de223b5abd18245dd4d4800e1fbc2a2523e31",
|
"sha256:6300e9ee9a404038113250bd218e2c4827f5e676efb14e77de2ad2dcb67679bc",
|
||||||
"sha256:fccfa81cda69bb2317ed97e7149d7d84d19e6ec3bfbe3f721139e7ac0c407c73"
|
"sha256:be4714f0475c1f5183eea09ddbf568ced6fa41b0fc9976f2698b8442e1b17303"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.17.98"
|
"version": "==1.17.102"
|
||||||
},
|
},
|
||||||
"botocore": {
|
"botocore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b2a49de4ee04b690142c8e7240f0f5758e3f7673dd39cf398efe893bf5e11c3f",
|
"sha256:2f57f7ceed1598d96cc497aeb45317db5d3b21a5aafea4732d0e561d0fc2a8fa",
|
||||||
"sha256:b955b23fe2fbdbbc8e66f37fe2970de6b5d8169f940b200bcf434751709d38f6"
|
"sha256:bdf08a4f7f01ead00d386848f089c08270499711447569c18d0db60023619c06"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||||
"version": "==1.20.98"
|
"version": "==1.20.102"
|
||||||
},
|
},
|
||||||
"cachetools": {
|
"cachetools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -165,11 +165,11 @@
|
||||||
},
|
},
|
||||||
"celery": {
|
"celery": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:54436cd97b031bf2e08064223240e2a83d601d9414bcb1b702f94c6c33c29485",
|
"sha256:8d9a3de9162965e97f8e8cc584c67aad83b3f7a267584fa47701ed11c3e0d4b0",
|
||||||
"sha256:b5399d76cf70d5cfac3ec993f8796ec1aa90d4cef55972295751f384758a80d7"
|
"sha256:9dab2170b4038f7bf10ef2861dbf486ddf1d20592290a1040f7b7a1259705d42"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==5.1.1"
|
"version": "==5.1.2"
|
||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -948,10 +948,30 @@
|
||||||
},
|
},
|
||||||
"pyrsistent": {
|
"pyrsistent": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"
|
"sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2",
|
||||||
|
"sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7",
|
||||||
|
"sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea",
|
||||||
|
"sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426",
|
||||||
|
"sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710",
|
||||||
|
"sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1",
|
||||||
|
"sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396",
|
||||||
|
"sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2",
|
||||||
|
"sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680",
|
||||||
|
"sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35",
|
||||||
|
"sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427",
|
||||||
|
"sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b",
|
||||||
|
"sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b",
|
||||||
|
"sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f",
|
||||||
|
"sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef",
|
||||||
|
"sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c",
|
||||||
|
"sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4",
|
||||||
|
"sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d",
|
||||||
|
"sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78",
|
||||||
|
"sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b",
|
||||||
|
"sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.5'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==0.17.3"
|
"version": "==0.18.0"
|
||||||
},
|
},
|
||||||
"python-dateutil": {
|
"python-dateutil": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1167,11 +1187,11 @@
|
||||||
"secure"
|
"secure"
|
||||||
],
|
],
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
|
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
|
||||||
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
|
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.26.5"
|
"version": "==1.26.6"
|
||||||
},
|
},
|
||||||
"uvicorn": {
|
"uvicorn": {
|
||||||
"extras": [
|
"extras": [
|
||||||
|
@ -1565,7 +1585,7 @@
|
||||||
"sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56",
|
"sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56",
|
||||||
"sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c"
|
"sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c"
|
||||||
],
|
],
|
||||||
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
|
"markers": "python_version < '4' and python_full_version >= '3.6.1'",
|
||||||
"version": "==5.9.1"
|
"version": "==5.9.1"
|
||||||
},
|
},
|
||||||
"lazy-object-proxy": {
|
"lazy-object-proxy": {
|
||||||
|
@ -1838,11 +1858,11 @@
|
||||||
"secure"
|
"secure"
|
||||||
],
|
],
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
|
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
|
||||||
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
|
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.26.5"
|
"version": "==1.26.6"
|
||||||
},
|
},
|
||||||
"wrapt": {
|
"wrapt": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|
|
@ -19,7 +19,7 @@ def token_from_header(raw_header: bytes) -> Optional[Token]:
|
||||||
auth_credentials = raw_header.decode()
|
auth_credentials = raw_header.decode()
|
||||||
if auth_credentials == "" or " " not in auth_credentials:
|
if auth_credentials == "" or " " not in auth_credentials:
|
||||||
return None
|
return None
|
||||||
auth_type, auth_credentials = auth_credentials.split()
|
auth_type, _, auth_credentials = auth_credentials.partition(" ")
|
||||||
if auth_type.lower() not in ["basic", "bearer"]:
|
if auth_type.lower() not in ["basic", "bearer"]:
|
||||||
LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower())
|
LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower())
|
||||||
raise AuthenticationFailed("Unsupported authentication type")
|
raise AuthenticationFailed("Unsupported authentication type")
|
||||||
|
|
|
@ -213,7 +213,7 @@ class SourceFlowManager:
|
||||||
planner = FlowPlanner(flow)
|
planner = FlowPlanner(flow)
|
||||||
plan = planner.plan(self.request, kwargs)
|
plan = planner.plan(self.request, kwargs)
|
||||||
for stage in self.get_stages_to_append(flow):
|
for stage in self.get_stages_to_append(flow):
|
||||||
plan.append(stage)
|
plan.append_stage(stage=stage)
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<script src="{% static 'dist/FlowInterface.js' %}?v={{ ak_version }}" type="module"></script>
|
<script src="{% static 'dist/FlowInterface.js' %}?v={{ ak_version }}" type="module"></script>
|
||||||
<style>
|
<style>
|
||||||
.pf-c-background-image::before {
|
.pf-c-background-image::before {
|
||||||
background-image: url("{{ flow.background_url }}");
|
--ak-flow-background: url("{{ flow.background_url }}");
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<style>
|
<style>
|
||||||
.pf-c-background-image::before {
|
.pf-c-background-image::before {
|
||||||
background-image: url("/static/dist/assets/images/flow_background.jpg");
|
--ak-flow-background: url("/static/dist/assets/images/flow_background.jpg");
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -6,11 +6,11 @@ from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||||
from guardian.shortcuts import get_objects_for_user
|
from guardian.shortcuts import get_objects_for_user
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.fields import CharField, DictField, IntegerField
|
from rest_framework.fields import DictField, IntegerField
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer, TypeCreateSerializer
|
from authentik.core.api.utils import PassiveSerializer, TypeCreateSerializer
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
|
@ -19,11 +19,6 @@ from authentik.events.models import Event, EventAction
|
||||||
class EventSerializer(ModelSerializer):
|
class EventSerializer(ModelSerializer):
|
||||||
"""Event Serializer"""
|
"""Event Serializer"""
|
||||||
|
|
||||||
# Since we only use this serializer for read-only operations,
|
|
||||||
# no checking of the action is done here.
|
|
||||||
# This allows clients to check wildcards, prefixes and custom types
|
|
||||||
action = CharField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
model = Event
|
model = Event
|
||||||
|
@ -96,7 +91,7 @@ class EventsFilter(django_filters.FilterSet):
|
||||||
fields = ["action", "client_ip", "username"]
|
fields = ["action", "client_ip", "username"]
|
||||||
|
|
||||||
|
|
||||||
class EventViewSet(ReadOnlyModelViewSet):
|
class EventViewSet(ModelViewSet):
|
||||||
"""Event Read-Only Viewset"""
|
"""Event Read-Only Viewset"""
|
||||||
|
|
||||||
queryset = Event.objects.all()
|
queryset = Event.objects.all()
|
||||||
|
|
|
@ -13,6 +13,7 @@ from authentik.core.models import User
|
||||||
from authentik.events.models import Event, EventAction, Notification
|
from authentik.events.models import Event, EventAction, Notification
|
||||||
from authentik.events.signals import EventNewThread
|
from authentik.events.signals import EventNewThread
|
||||||
from authentik.events.utils import model_to_dict
|
from authentik.events.utils import model_to_dict
|
||||||
|
from authentik.lib.sentry import before_send
|
||||||
from authentik.lib.utils.errors import exception_to_string
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,12 +63,13 @@ class AuditMiddleware:
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
return
|
return
|
||||||
thread = EventNewThread(
|
if before_send({}, {"exc_info": (None, exception, None)}) is not None:
|
||||||
EventAction.SYSTEM_EXCEPTION,
|
thread = EventNewThread(
|
||||||
request,
|
EventAction.SYSTEM_EXCEPTION,
|
||||||
message=exception_to_string(exception),
|
request,
|
||||||
)
|
message=exception_to_string(exception),
|
||||||
thread.run()
|
)
|
||||||
|
thread.run()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
|
|
|
@ -105,7 +105,11 @@ def notification_transport(
|
||||||
"""Send notification over specified transport"""
|
"""Send notification over specified transport"""
|
||||||
self.save_on_success = False
|
self.save_on_success = False
|
||||||
try:
|
try:
|
||||||
notification: Notification = Notification.objects.get(pk=notification_pk)
|
notification: Notification = Notification.objects.filter(
|
||||||
|
pk=notification_pk
|
||||||
|
).first()
|
||||||
|
if not notification:
|
||||||
|
return
|
||||||
transport: NotificationTransport = NotificationTransport.objects.get(
|
transport: NotificationTransport = NotificationTransport.objects.get(
|
||||||
pk=transport_pk
|
pk=transport_pk
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,6 +25,7 @@ class FlowStageBindingSerializer(ModelSerializer):
|
||||||
"re_evaluate_policies",
|
"re_evaluate_policies",
|
||||||
"order",
|
"order",
|
||||||
"policy_engine_mode",
|
"policy_engine_mode",
|
||||||
|
"invalid_response_action",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@ from typing import TYPE_CHECKING, Optional
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.flows.models import FlowStageBinding
|
||||||
from authentik.flows.models import Stage
|
|
||||||
from authentik.policies.engine import PolicyEngine
|
from authentik.policies.engine import PolicyEngine
|
||||||
from authentik.policies.models import PolicyBinding
|
from authentik.policies.models import PolicyBinding
|
||||||
|
|
||||||
|
@ -22,11 +21,14 @@ class StageMarker:
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def process(
|
def process(
|
||||||
self, plan: "FlowPlan", stage: Stage, http_request: Optional[HttpRequest]
|
self,
|
||||||
) -> Optional[Stage]:
|
plan: "FlowPlan",
|
||||||
|
binding: FlowStageBinding,
|
||||||
|
http_request: HttpRequest,
|
||||||
|
) -> Optional[FlowStageBinding]:
|
||||||
"""Process callback for this marker. This should be overridden by sub-classes.
|
"""Process callback for this marker. This should be overridden by sub-classes.
|
||||||
If a stage should be removed, return None."""
|
If a stage should be removed, return None."""
|
||||||
return stage
|
return binding
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -34,24 +36,34 @@ class ReevaluateMarker(StageMarker):
|
||||||
"""Reevaluate Marker, forces stage's policies to be evaluated again."""
|
"""Reevaluate Marker, forces stage's policies to be evaluated again."""
|
||||||
|
|
||||||
binding: PolicyBinding
|
binding: PolicyBinding
|
||||||
user: User
|
|
||||||
|
|
||||||
def process(
|
def process(
|
||||||
self, plan: "FlowPlan", stage: Stage, http_request: Optional[HttpRequest]
|
self,
|
||||||
) -> Optional[Stage]:
|
plan: "FlowPlan",
|
||||||
|
binding: FlowStageBinding,
|
||||||
|
http_request: HttpRequest,
|
||||||
|
) -> Optional[FlowStageBinding]:
|
||||||
"""Re-evaluate policies bound to stage, and if they fail, remove from plan"""
|
"""Re-evaluate policies bound to stage, and if they fail, remove from plan"""
|
||||||
engine = PolicyEngine(self.binding, self.user)
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
|
|
||||||
|
LOGGER.debug(
|
||||||
|
"f(plan_inst)[re-eval marker]: running re-evaluation",
|
||||||
|
binding=binding,
|
||||||
|
policy_binding=self.binding,
|
||||||
|
)
|
||||||
|
engine = PolicyEngine(
|
||||||
|
self.binding, plan.context.get(PLAN_CONTEXT_PENDING_USER, http_request.user)
|
||||||
|
)
|
||||||
engine.use_cache = False
|
engine.use_cache = False
|
||||||
if http_request:
|
engine.request.set_http_request(http_request)
|
||||||
engine.request.set_http_request(http_request)
|
|
||||||
engine.request.context = plan.context
|
engine.request.context = plan.context
|
||||||
engine.build()
|
engine.build()
|
||||||
result = engine.result
|
result = engine.result
|
||||||
if result.passing:
|
if result.passing:
|
||||||
return stage
|
return binding
|
||||||
LOGGER.warning(
|
LOGGER.warning(
|
||||||
"f(plan_inst)[re-eval marker]: stage failed re-evaluation",
|
"f(plan_inst)[re-eval marker]: binding failed re-evaluation",
|
||||||
stage=stage,
|
binding=binding,
|
||||||
messages=result.messages,
|
messages=result.messages,
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -135,7 +135,7 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("authentik_flows", "0017_auto_20210329_1334"),
|
("authentik_flows", "0017_auto_20210329_1334"),
|
||||||
("authentik_stages_user_write", "__latest__"),
|
("authentik_stages_user_write", "0002_auto_20200918_1653"),
|
||||||
("authentik_stages_user_login", "__latest__"),
|
("authentik_stages_user_login", "__latest__"),
|
||||||
("authentik_stages_password", "0002_passwordstage_change_flow"),
|
("authentik_stages_password", "0002_passwordstage_change_flow"),
|
||||||
("authentik_policies", "0001_initial"),
|
("authentik_policies", "0001_initial"),
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-06-27 16:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_flows", "0020_flow_compatibility_mode"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="flowstagebinding",
|
||||||
|
name="invalid_response_action",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[("retry", "Retry"), ("continue", "Continue")],
|
||||||
|
default="retry",
|
||||||
|
help_text="Configure how the flow executor should handle an invalid response to a challenge. RETRY returns the error message and a similar challenge to the executor while CONTINUE continues with the next stage.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -27,6 +27,14 @@ class NotConfiguredAction(models.TextChoices):
|
||||||
CONFIGURE = "configure"
|
CONFIGURE = "configure"
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidResponseAction(models.TextChoices):
|
||||||
|
"""Configure how the flow executor should handle invalid responses to challenges"""
|
||||||
|
|
||||||
|
RETRY = "retry"
|
||||||
|
RESTART = "restart"
|
||||||
|
RESTART_WITH_CONTEXT = "restart_with_context"
|
||||||
|
|
||||||
|
|
||||||
class FlowDesignation(models.TextChoices):
|
class FlowDesignation(models.TextChoices):
|
||||||
"""Designation of what a Flow should be used for. At a later point, this
|
"""Designation of what a Flow should be used for. At a later point, this
|
||||||
should be replaced by a database entry."""
|
should be replaced by a database entry."""
|
||||||
|
@ -201,6 +209,17 @@ class FlowStageBinding(SerializerModel, PolicyBindingModel):
|
||||||
help_text=_("Evaluate policies when the Stage is present to the user."),
|
help_text=_("Evaluate policies when the Stage is present to the user."),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
invalid_response_action = models.TextField(
|
||||||
|
choices=InvalidResponseAction.choices,
|
||||||
|
default=InvalidResponseAction.RETRY,
|
||||||
|
help_text=_(
|
||||||
|
"Configure how the flow executor should handle an invalid response to a "
|
||||||
|
"challenge. RETRY returns the error message and a similar challenge to the "
|
||||||
|
"executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT "
|
||||||
|
"restarts the flow while keeping the current context."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
order = models.IntegerField()
|
order = models.IntegerField()
|
||||||
|
|
||||||
objects = InheritanceManager()
|
objects = InheritanceManager()
|
||||||
|
|
|
@ -52,33 +52,41 @@ class FlowPlan:
|
||||||
|
|
||||||
flow_pk: str
|
flow_pk: str
|
||||||
|
|
||||||
stages: list[Stage] = field(default_factory=list)
|
bindings: list[FlowStageBinding] = field(default_factory=list)
|
||||||
context: dict[str, Any] = field(default_factory=dict)
|
context: dict[str, Any] = field(default_factory=dict)
|
||||||
markers: list[StageMarker] = field(default_factory=list)
|
markers: list[StageMarker] = field(default_factory=list)
|
||||||
|
|
||||||
def append(self, stage: Stage, marker: Optional[StageMarker] = None):
|
def append_stage(self, stage: Stage, marker: Optional[StageMarker] = None):
|
||||||
"""Append `stage` to all stages, optionall with stage marker"""
|
"""Append `stage` to all stages, optionall with stage marker"""
|
||||||
self.stages.append(stage)
|
return self.append(FlowStageBinding(stage=stage), marker)
|
||||||
|
|
||||||
|
def append(self, binding: FlowStageBinding, marker: Optional[StageMarker] = None):
|
||||||
|
"""Append `stage` to all stages, optionall with stage marker"""
|
||||||
|
self.bindings.append(binding)
|
||||||
self.markers.append(marker or StageMarker())
|
self.markers.append(marker or StageMarker())
|
||||||
|
|
||||||
def insert(self, stage: Stage, marker: Optional[StageMarker] = None):
|
def insert_stage(self, stage: Stage, marker: Optional[StageMarker] = None):
|
||||||
"""Insert stage into plan, as immediate next stage"""
|
"""Insert stage into plan, as immediate next stage"""
|
||||||
self.stages.insert(1, stage)
|
self.bindings.insert(1, FlowStageBinding(stage=stage, order=0))
|
||||||
self.markers.insert(1, marker or StageMarker())
|
self.markers.insert(1, marker or StageMarker())
|
||||||
|
|
||||||
def next(self, http_request: Optional[HttpRequest]) -> Optional[Stage]:
|
def next(self, http_request: Optional[HttpRequest]) -> Optional[FlowStageBinding]:
|
||||||
"""Return next pending stage from the bottom of the list"""
|
"""Return next pending stage from the bottom of the list"""
|
||||||
if not self.has_stages:
|
if not self.has_stages:
|
||||||
return None
|
return None
|
||||||
stage = self.stages[0]
|
binding = self.bindings[0]
|
||||||
marker = self.markers[0]
|
marker = self.markers[0]
|
||||||
|
|
||||||
if marker.__class__ is not StageMarker:
|
if marker.__class__ is not StageMarker:
|
||||||
LOGGER.debug("f(plan_inst): stage has marker", stage=stage, marker=marker)
|
LOGGER.debug(
|
||||||
marked_stage = marker.process(self, stage, http_request)
|
"f(plan_inst): stage has marker", binding=binding, marker=marker
|
||||||
|
)
|
||||||
|
marked_stage = marker.process(self, binding, http_request)
|
||||||
if not marked_stage:
|
if not marked_stage:
|
||||||
LOGGER.debug("f(plan_inst): marker returned none, next stage", stage=stage)
|
LOGGER.debug(
|
||||||
self.stages.remove(stage)
|
"f(plan_inst): marker returned none, next stage", binding=binding
|
||||||
|
)
|
||||||
|
self.bindings.remove(binding)
|
||||||
self.markers.remove(marker)
|
self.markers.remove(marker)
|
||||||
if not self.has_stages:
|
if not self.has_stages:
|
||||||
return None
|
return None
|
||||||
|
@ -89,12 +97,12 @@ class FlowPlan:
|
||||||
def pop(self):
|
def pop(self):
|
||||||
"""Pop next pending stage from bottom of list"""
|
"""Pop next pending stage from bottom of list"""
|
||||||
self.markers.pop(0)
|
self.markers.pop(0)
|
||||||
self.stages.pop(0)
|
self.bindings.pop(0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_stages(self) -> bool:
|
def has_stages(self) -> bool:
|
||||||
"""Check if there are any stages left in this plan"""
|
"""Check if there are any stages left in this plan"""
|
||||||
return len(self.markers) + len(self.stages) > 0
|
return len(self.markers) + len(self.bindings) > 0
|
||||||
|
|
||||||
|
|
||||||
class FlowPlanner:
|
class FlowPlanner:
|
||||||
|
@ -161,7 +169,7 @@ class FlowPlanner:
|
||||||
plan = self._build_plan(user, request, default_context)
|
plan = self._build_plan(user, request, default_context)
|
||||||
cache.set(cache_key(self.flow, user), plan, CACHE_TIMEOUT)
|
cache.set(cache_key(self.flow, user), plan, CACHE_TIMEOUT)
|
||||||
GAUGE_FLOWS_CACHED.update()
|
GAUGE_FLOWS_CACHED.update()
|
||||||
if not plan.stages and not self.allow_empty_flows:
|
if not plan.bindings and not self.allow_empty_flows:
|
||||||
raise EmptyFlowException()
|
raise EmptyFlowException()
|
||||||
return plan
|
return plan
|
||||||
|
|
||||||
|
@ -216,9 +224,9 @@ class FlowPlanner:
|
||||||
"f(plan): stage has re-evaluate marker",
|
"f(plan): stage has re-evaluate marker",
|
||||||
stage=binding.stage,
|
stage=binding.stage,
|
||||||
)
|
)
|
||||||
marker = ReevaluateMarker(binding=binding, user=user)
|
marker = ReevaluateMarker(binding=binding)
|
||||||
if stage:
|
if stage:
|
||||||
plan.append(stage, marker)
|
plan.append(binding, marker)
|
||||||
HIST_FLOWS_PLAN_TIME.labels(flow_slug=self.flow.slug)
|
HIST_FLOWS_PLAN_TIME.labels(flow_slug=self.flow.slug)
|
||||||
self._logger.debug(
|
self._logger.debug(
|
||||||
"f(plan): finished building",
|
"f(plan): finished building",
|
||||||
|
|
|
@ -16,6 +16,7 @@ from authentik.flows.challenge import (
|
||||||
HttpChallengeResponse,
|
HttpChallengeResponse,
|
||||||
WithUserInfoChallenge,
|
WithUserInfoChallenge,
|
||||||
)
|
)
|
||||||
|
from authentik.flows.models import InvalidResponseAction
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.views import FlowExecutorView
|
from authentik.flows.views import FlowExecutorView
|
||||||
|
|
||||||
|
@ -69,7 +70,13 @@ class ChallengeStageView(StageView):
|
||||||
"""Return a challenge for the frontend to solve"""
|
"""Return a challenge for the frontend to solve"""
|
||||||
challenge = self._get_challenge(*args, **kwargs)
|
challenge = self._get_challenge(*args, **kwargs)
|
||||||
if not challenge.is_valid():
|
if not challenge.is_valid():
|
||||||
LOGGER.warning(challenge.errors, stage_view=self, challenge=challenge)
|
LOGGER.warning(
|
||||||
|
"f(ch): Invalid challenge",
|
||||||
|
binding=self.executor.current_binding,
|
||||||
|
errors=challenge.errors,
|
||||||
|
stage_view=self,
|
||||||
|
challenge=challenge,
|
||||||
|
)
|
||||||
return HttpChallengeResponse(challenge)
|
return HttpChallengeResponse(challenge)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
|
@ -77,6 +84,21 @@ class ChallengeStageView(StageView):
|
||||||
"""Handle challenge response"""
|
"""Handle challenge response"""
|
||||||
challenge: ChallengeResponse = self.get_response_instance(data=request.data)
|
challenge: ChallengeResponse = self.get_response_instance(data=request.data)
|
||||||
if not challenge.is_valid():
|
if not challenge.is_valid():
|
||||||
|
if self.executor.current_binding.invalid_response_action in [
|
||||||
|
InvalidResponseAction.RESTART,
|
||||||
|
InvalidResponseAction.RESTART_WITH_CONTEXT,
|
||||||
|
]:
|
||||||
|
keep_context = (
|
||||||
|
self.executor.current_binding.invalid_response_action
|
||||||
|
== InvalidResponseAction.RESTART_WITH_CONTEXT
|
||||||
|
)
|
||||||
|
LOGGER.debug(
|
||||||
|
"f(ch): Invalid response, restarting flow",
|
||||||
|
binding=self.executor.current_binding,
|
||||||
|
stage_view=self,
|
||||||
|
keep_context=keep_context,
|
||||||
|
)
|
||||||
|
return self.executor.restart_flow(keep_context)
|
||||||
return self.challenge_invalid(challenge)
|
return self.challenge_invalid(challenge)
|
||||||
return self.challenge_valid(challenge)
|
return self.challenge_valid(challenge)
|
||||||
|
|
||||||
|
@ -126,5 +148,10 @@ class ChallengeStageView(StageView):
|
||||||
)
|
)
|
||||||
challenge_response.initial_data["response_errors"] = full_errors
|
challenge_response.initial_data["response_errors"] = full_errors
|
||||||
if not challenge_response.is_valid():
|
if not challenge_response.is_valid():
|
||||||
LOGGER.warning(challenge_response.errors)
|
LOGGER.warning(
|
||||||
|
"f(ch): invalid challenge response",
|
||||||
|
binding=self.executor.current_binding,
|
||||||
|
errors=challenge_response.errors,
|
||||||
|
stage_view=self,
|
||||||
|
)
|
||||||
return HttpChallengeResponse(challenge_response)
|
return HttpChallengeResponse(challenge_response)
|
||||||
|
|
|
@ -182,8 +182,8 @@ class TestFlowPlanner(TestCase):
|
||||||
planner = FlowPlanner(flow)
|
planner = FlowPlanner(flow)
|
||||||
plan = planner.plan(request)
|
plan = planner.plan(request)
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding.stage)
|
self.assertEqual(plan.bindings[0], binding)
|
||||||
self.assertEqual(plan.stages[1], binding2.stage)
|
self.assertEqual(plan.bindings[1], binding2)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
||||||
|
|
|
@ -11,15 +11,23 @@ from authentik.core.models import User
|
||||||
from authentik.flows.challenge import ChallengeTypes
|
from authentik.flows.challenge import ChallengeTypes
|
||||||
from authentik.flows.exceptions import FlowNonApplicableException
|
from authentik.flows.exceptions import FlowNonApplicableException
|
||||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import (
|
||||||
|
Flow,
|
||||||
|
FlowDesignation,
|
||||||
|
FlowStageBinding,
|
||||||
|
InvalidResponseAction,
|
||||||
|
)
|
||||||
from authentik.flows.planner import FlowPlan, FlowPlanner
|
from authentik.flows.planner import FlowPlan, FlowPlanner
|
||||||
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView
|
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView
|
||||||
from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView
|
from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.policies.dummy.models import DummyPolicy
|
from authentik.policies.dummy.models import DummyPolicy
|
||||||
from authentik.policies.models import PolicyBinding
|
from authentik.policies.models import PolicyBinding
|
||||||
|
from authentik.policies.reputation.models import ReputationPolicy
|
||||||
from authentik.policies.types import PolicyResult
|
from authentik.policies.types import PolicyResult
|
||||||
|
from authentik.stages.deny.models import DenyStage
|
||||||
from authentik.stages.dummy.models import DummyStage
|
from authentik.stages.dummy.models import DummyStage
|
||||||
|
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||||
|
|
||||||
POLICY_RETURN_FALSE = PropertyMock(return_value=PolicyResult(False))
|
POLICY_RETURN_FALSE = PropertyMock(return_value=PolicyResult(False))
|
||||||
POLICY_RETURN_TRUE = MagicMock(return_value=PolicyResult(True))
|
POLICY_RETURN_TRUE = MagicMock(return_value=PolicyResult(True))
|
||||||
|
@ -52,8 +60,9 @@ class TestFlowExecutor(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
stage = DummyStage.objects.create(name="dummy")
|
stage = DummyStage.objects.create(name="dummy")
|
||||||
|
binding = FlowStageBinding(target=flow, stage=stage, order=0)
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=flow.pk.hex + "a", stages=[stage], markers=[StageMarker()]
|
flow_pk=flow.pk.hex + "a", bindings=[binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -163,7 +172,7 @@ class TestFlowExecutor(TestCase):
|
||||||
# Check that two stages are in plan
|
# Check that two stages are in plan
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
||||||
self.assertEqual(len(plan.stages), 2)
|
self.assertEqual(len(plan.bindings), 2)
|
||||||
# Second request, submit form, one stage left
|
# Second request, submit form, one stage left
|
||||||
response = self.client.post(exec_url)
|
response = self.client.post(exec_url)
|
||||||
# Second request redirects to the same URL
|
# Second request redirects to the same URL
|
||||||
|
@ -172,7 +181,7 @@ class TestFlowExecutor(TestCase):
|
||||||
# Check that two stages are in plan
|
# Check that two stages are in plan
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
||||||
self.assertEqual(len(plan.stages), 1)
|
self.assertEqual(len(plan.bindings), 1)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"authentik.flows.views.to_stage_response",
|
"authentik.flows.views.to_stage_response",
|
||||||
|
@ -213,8 +222,8 @@ class TestFlowExecutor(TestCase):
|
||||||
|
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding.stage)
|
self.assertEqual(plan.bindings[0], binding)
|
||||||
self.assertEqual(plan.stages[1], binding2.stage)
|
self.assertEqual(plan.bindings[1], binding2)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
||||||
|
@ -267,9 +276,9 @@ class TestFlowExecutor(TestCase):
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding.stage)
|
self.assertEqual(plan.bindings[0], binding)
|
||||||
self.assertEqual(plan.stages[1], binding2.stage)
|
self.assertEqual(plan.bindings[1], binding2)
|
||||||
self.assertEqual(plan.stages[2], binding3.stage)
|
self.assertEqual(plan.bindings[2], binding3)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
||||||
|
@ -281,8 +290,8 @@ class TestFlowExecutor(TestCase):
|
||||||
|
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding2.stage)
|
self.assertEqual(plan.bindings[0], binding2)
|
||||||
self.assertEqual(plan.stages[1], binding3.stage)
|
self.assertEqual(plan.bindings[1], binding3)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
self.assertIsInstance(plan.markers[1], StageMarker)
|
self.assertIsInstance(plan.markers[1], StageMarker)
|
||||||
|
@ -338,9 +347,9 @@ class TestFlowExecutor(TestCase):
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding.stage)
|
self.assertEqual(plan.bindings[0], binding)
|
||||||
self.assertEqual(plan.stages[1], binding2.stage)
|
self.assertEqual(plan.bindings[1], binding2)
|
||||||
self.assertEqual(plan.stages[2], binding3.stage)
|
self.assertEqual(plan.bindings[2], binding3)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
||||||
|
@ -352,8 +361,8 @@ class TestFlowExecutor(TestCase):
|
||||||
|
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding2.stage)
|
self.assertEqual(plan.bindings[0], binding2)
|
||||||
self.assertEqual(plan.stages[1], binding3.stage)
|
self.assertEqual(plan.bindings[1], binding3)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
self.assertIsInstance(plan.markers[1], StageMarker)
|
self.assertIsInstance(plan.markers[1], StageMarker)
|
||||||
|
@ -364,7 +373,7 @@ class TestFlowExecutor(TestCase):
|
||||||
|
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding3.stage)
|
self.assertEqual(plan.bindings[0], binding3)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
|
|
||||||
|
@ -438,10 +447,10 @@ class TestFlowExecutor(TestCase):
|
||||||
|
|
||||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||||
|
|
||||||
self.assertEqual(plan.stages[0], binding.stage)
|
self.assertEqual(plan.bindings[0], binding)
|
||||||
self.assertEqual(plan.stages[1], binding2.stage)
|
self.assertEqual(plan.bindings[1], binding2)
|
||||||
self.assertEqual(plan.stages[2], binding3.stage)
|
self.assertEqual(plan.bindings[2], binding3)
|
||||||
self.assertEqual(plan.stages[3], binding4.stage)
|
self.assertEqual(plan.bindings[3], binding4)
|
||||||
|
|
||||||
self.assertIsInstance(plan.markers[0], StageMarker)
|
self.assertIsInstance(plan.markers[0], StageMarker)
|
||||||
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
|
||||||
|
@ -512,3 +521,78 @@ class TestFlowExecutor(TestCase):
|
||||||
|
|
||||||
stage_view = StageView(executor)
|
stage_view = StageView(executor)
|
||||||
self.assertEqual(ident, stage_view.get_pending_user(for_display=True).username)
|
self.assertEqual(ident, stage_view.get_pending_user(for_display=True).username)
|
||||||
|
|
||||||
|
def test_invalid_restart(self):
|
||||||
|
"""Test flow that restarts on invalid entry"""
|
||||||
|
flow = Flow.objects.create(
|
||||||
|
name="restart-on-invalid",
|
||||||
|
slug="restart-on-invalid",
|
||||||
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
|
)
|
||||||
|
# Stage 0 is a deny stage that is added dynamically
|
||||||
|
# when the reputation policy says so
|
||||||
|
deny_stage = DenyStage.objects.create(name="deny")
|
||||||
|
reputation_policy = ReputationPolicy.objects.create(
|
||||||
|
name="reputation", threshold=-1, check_ip=False
|
||||||
|
)
|
||||||
|
deny_binding = FlowStageBinding.objects.create(
|
||||||
|
target=flow,
|
||||||
|
stage=deny_stage,
|
||||||
|
order=0,
|
||||||
|
evaluate_on_plan=False,
|
||||||
|
re_evaluate_policies=True,
|
||||||
|
)
|
||||||
|
PolicyBinding.objects.create(
|
||||||
|
policy=reputation_policy, target=deny_binding, order=0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Stage 1 is an identification stage
|
||||||
|
ident_stage = IdentificationStage.objects.create(
|
||||||
|
name="ident",
|
||||||
|
user_fields=[UserFields.E_MAIL],
|
||||||
|
)
|
||||||
|
FlowStageBinding.objects.create(
|
||||||
|
target=flow,
|
||||||
|
stage=ident_stage,
|
||||||
|
order=1,
|
||||||
|
invalid_response_action=InvalidResponseAction.RESTART_WITH_CONTEXT,
|
||||||
|
)
|
||||||
|
exec_url = reverse(
|
||||||
|
"authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}
|
||||||
|
)
|
||||||
|
# First request, run the planner
|
||||||
|
response = self.client.get(exec_url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertJSONEqual(
|
||||||
|
force_str(response.content),
|
||||||
|
{
|
||||||
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
|
"component": "ak-stage-identification",
|
||||||
|
"flow_info": {
|
||||||
|
"background": flow.background_url,
|
||||||
|
"cancel_url": reverse("authentik_flows:cancel"),
|
||||||
|
"title": "",
|
||||||
|
},
|
||||||
|
"password_fields": False,
|
||||||
|
"primary_action": "Log in",
|
||||||
|
"sources": [],
|
||||||
|
"user_fields": [UserFields.E_MAIL],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
response = self.client.post(
|
||||||
|
exec_url, {"uid_field": "invalid-string"}, follow=True
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertJSONEqual(
|
||||||
|
force_str(response.content),
|
||||||
|
{
|
||||||
|
"component": "ak-stage-access-denied",
|
||||||
|
"error_message": None,
|
||||||
|
"flow_info": {
|
||||||
|
"background": flow.background_url,
|
||||||
|
"cancel_url": reverse("authentik_flows:cancel"),
|
||||||
|
"title": "",
|
||||||
|
},
|
||||||
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ from typing import Any, Optional
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.core.cache import cache
|
||||||
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
|
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
|
||||||
from django.http.request import QueryDict
|
from django.http.request import QueryDict
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
@ -37,7 +38,13 @@ from authentik.flows.challenge import (
|
||||||
WithUserInfoChallenge,
|
WithUserInfoChallenge,
|
||||||
)
|
)
|
||||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||||
from authentik.flows.models import ConfigurableStage, Flow, FlowDesignation, Stage
|
from authentik.flows.models import (
|
||||||
|
ConfigurableStage,
|
||||||
|
Flow,
|
||||||
|
FlowDesignation,
|
||||||
|
FlowStageBinding,
|
||||||
|
Stage,
|
||||||
|
)
|
||||||
from authentik.flows.planner import (
|
from authentik.flows.planner import (
|
||||||
PLAN_CONTEXT_PENDING_USER,
|
PLAN_CONTEXT_PENDING_USER,
|
||||||
PLAN_CONTEXT_REDIRECT,
|
PLAN_CONTEXT_REDIRECT,
|
||||||
|
@ -107,6 +114,7 @@ class FlowExecutorView(APIView):
|
||||||
flow: Flow
|
flow: Flow
|
||||||
|
|
||||||
plan: Optional[FlowPlan] = None
|
plan: Optional[FlowPlan] = None
|
||||||
|
current_binding: FlowStageBinding
|
||||||
current_stage: Stage
|
current_stage: Stage
|
||||||
current_stage_view: View
|
current_stage_view: View
|
||||||
|
|
||||||
|
@ -159,11 +167,12 @@ class FlowExecutorView(APIView):
|
||||||
request.session[SESSION_KEY_GET] = QueryDict(request.GET.get("query", ""))
|
request.session[SESSION_KEY_GET] = QueryDict(request.GET.get("query", ""))
|
||||||
# We don't save the Plan after getting the next stage
|
# We don't save the Plan after getting the next stage
|
||||||
# as it hasn't been successfully passed yet
|
# as it hasn't been successfully passed yet
|
||||||
next_stage = self.plan.next(self.request)
|
next_binding = self.plan.next(self.request)
|
||||||
if not next_stage:
|
if not next_binding:
|
||||||
self._logger.debug("f(exec): no more stages, flow is done.")
|
self._logger.debug("f(exec): no more stages, flow is done.")
|
||||||
return self._flow_done()
|
return self._flow_done()
|
||||||
self.current_stage = next_stage
|
self.current_binding = next_binding
|
||||||
|
self.current_stage = next_binding.stage
|
||||||
self._logger.debug(
|
self._logger.debug(
|
||||||
"f(exec): Current stage",
|
"f(exec): Current stage",
|
||||||
current_stage=self.current_stage,
|
current_stage=self.current_stage,
|
||||||
|
@ -268,8 +277,31 @@ class FlowExecutorView(APIView):
|
||||||
planner = FlowPlanner(self.flow)
|
planner = FlowPlanner(self.flow)
|
||||||
plan = planner.plan(self.request)
|
plan = planner.plan(self.request)
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
|
try:
|
||||||
|
# Call the has_stages getter to check that
|
||||||
|
# there are no issues with the class we might've gotten
|
||||||
|
# from the cache. If there are errors, just delete all cached flows
|
||||||
|
_ = plan.has_stages
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
keys = cache.keys("flow_*")
|
||||||
|
cache.delete_many(keys)
|
||||||
|
return self._initiate_plan()
|
||||||
return plan
|
return plan
|
||||||
|
|
||||||
|
def restart_flow(self, keep_context=False) -> HttpResponse:
|
||||||
|
"""Restart the currently active flow, optionally keeping the current context"""
|
||||||
|
planner = FlowPlanner(self.flow)
|
||||||
|
default_context = None
|
||||||
|
if keep_context:
|
||||||
|
default_context = self.plan.context
|
||||||
|
plan = planner.plan(self.request, default_context)
|
||||||
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
|
kwargs = self.kwargs
|
||||||
|
kwargs.update({"flow_slug": self.flow.slug})
|
||||||
|
return redirect_with_qs(
|
||||||
|
"authentik_api:flow-executor", self.request.GET, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def _flow_done(self) -> HttpResponse:
|
def _flow_done(self) -> HttpResponse:
|
||||||
"""User Successfully passed all stages"""
|
"""User Successfully passed all stages"""
|
||||||
# Since this is wrapped by the ExecutorShell, the next argument is saved in the session
|
# Since this is wrapped by the ExecutorShell, the next argument is saved in the session
|
||||||
|
@ -293,10 +325,10 @@ class FlowExecutorView(APIView):
|
||||||
)
|
)
|
||||||
self.plan.pop()
|
self.plan.pop()
|
||||||
self.request.session[SESSION_KEY_PLAN] = self.plan
|
self.request.session[SESSION_KEY_PLAN] = self.plan
|
||||||
if self.plan.stages:
|
if self.plan.bindings:
|
||||||
self._logger.debug(
|
self._logger.debug(
|
||||||
"f(exec): Continuing with next stage",
|
"f(exec): Continuing with next stage",
|
||||||
remaining=len(self.plan.stages),
|
remaining=len(self.plan.bindings),
|
||||||
)
|
)
|
||||||
kwargs = self.kwargs
|
kwargs = self.kwargs
|
||||||
kwargs.update({"flow_slug": self.flow.slug})
|
kwargs.update({"flow_slug": self.flow.slug})
|
||||||
|
|
|
@ -53,6 +53,27 @@ class DockerController(BaseController):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _comp_ports(self, container: Container) -> bool:
|
||||||
|
"""Check that the container has the correct ports exposed. Return true if container needs
|
||||||
|
to be rebuilt."""
|
||||||
|
# with TEST enabled, we use host-network
|
||||||
|
if settings.TEST:
|
||||||
|
return False
|
||||||
|
# When the container isn't running, the API doesn't report any port mappings
|
||||||
|
if container.status != "running":
|
||||||
|
return False
|
||||||
|
# {'6379/tcp': [{'HostIp': '127.0.0.1', 'HostPort': '6379'}]}
|
||||||
|
for port in self.deployment_ports:
|
||||||
|
key = f"{port.inner_port or port.port}/{port.protocol.lower()}"
|
||||||
|
if key not in container.ports:
|
||||||
|
return True
|
||||||
|
host_matching = False
|
||||||
|
for host_port in container.ports[key]:
|
||||||
|
host_matching = host_port.get("HostPort") == port.port
|
||||||
|
if not host_matching:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _get_container(self) -> tuple[Container, bool]:
|
def _get_container(self) -> tuple[Container, bool]:
|
||||||
container_name = f"authentik-proxy-{self.outpost.uuid.hex}"
|
container_name = f"authentik-proxy-{self.outpost.uuid.hex}"
|
||||||
try:
|
try:
|
||||||
|
@ -98,6 +119,11 @@ class DockerController(BaseController):
|
||||||
)
|
)
|
||||||
self.down()
|
self.down()
|
||||||
return self.up()
|
return self.up()
|
||||||
|
# Check container's ports
|
||||||
|
if self._comp_ports(container):
|
||||||
|
self.logger.info("Container has mis-matched ports, re-creating...")
|
||||||
|
self.down()
|
||||||
|
return self.up()
|
||||||
# Check that container values match our values
|
# Check that container values match our values
|
||||||
if self._comp_env(container):
|
if self._comp_env(container):
|
||||||
self.logger.info("Container has outdated config, re-creating...")
|
self.logger.info("Container has outdated config, re-creating...")
|
||||||
|
|
|
@ -406,7 +406,10 @@ class Outpost(ManagedModel):
|
||||||
|
|
||||||
def get_required_objects(self) -> Iterable[Union[models.Model, str]]:
|
def get_required_objects(self) -> Iterable[Union[models.Model, str]]:
|
||||||
"""Get an iterator of all objects the user needs read access to"""
|
"""Get an iterator of all objects the user needs read access to"""
|
||||||
objects: list[Union[models.Model, str]] = [self]
|
objects: list[Union[models.Model, str]] = [
|
||||||
|
self,
|
||||||
|
"authentik_events.add_event",
|
||||||
|
]
|
||||||
for provider in (
|
for provider in (
|
||||||
Provider.objects.filter(outpost=self).select_related().select_subclasses()
|
Provider.objects.filter(outpost=self).select_related().select_subclasses()
|
||||||
):
|
):
|
||||||
|
|
|
@ -33,21 +33,21 @@ class ReputationPolicy(Policy):
|
||||||
|
|
||||||
def passes(self, request: PolicyRequest) -> PolicyResult:
|
def passes(self, request: PolicyRequest) -> PolicyResult:
|
||||||
remote_ip = get_client_ip(request.http_request)
|
remote_ip = get_client_ip(request.http_request)
|
||||||
passing = True
|
passing = False
|
||||||
if self.check_ip:
|
if self.check_ip:
|
||||||
score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0)
|
score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0)
|
||||||
passing = passing and score <= self.threshold
|
passing += passing or score <= self.threshold
|
||||||
LOGGER.debug("Score for IP", ip=remote_ip, score=score, passing=passing)
|
LOGGER.debug("Score for IP", ip=remote_ip, score=score, passing=passing)
|
||||||
if self.check_username:
|
if self.check_username:
|
||||||
score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0)
|
score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0)
|
||||||
passing = passing and score <= self.threshold
|
passing += passing or score <= self.threshold
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Score for Username",
|
"Score for Username",
|
||||||
username=request.user.username,
|
username=request.user.username,
|
||||||
score=score,
|
score=score,
|
||||||
passing=passing,
|
passing=passing,
|
||||||
)
|
)
|
||||||
return PolicyResult(passing)
|
return PolicyResult(bool(passing))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
|
|
|
@ -474,7 +474,7 @@ class RefreshToken(ExpiringModel, BaseGrantModel):
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
iat_time = now
|
iat_time = now
|
||||||
exp_time = int(
|
exp_time = int(
|
||||||
now + timedelta_from_string(self.provider.token_validity).seconds
|
now + timedelta_from_string(self.provider.token_validity).total_seconds()
|
||||||
)
|
)
|
||||||
# We use the timestamp of the user's last successful login (EventAction.LOGIN) for auth_time
|
# We use the timestamp of the user's last successful login (EventAction.LOGIN) for auth_time
|
||||||
auth_events = Event.objects.filter(
|
auth_events = Event.objects.filter(
|
||||||
|
|
|
@ -374,9 +374,9 @@ class OAuthFulfillmentStage(StageView):
|
||||||
query_fragment["code"] = code.code
|
query_fragment["code"] = code.code
|
||||||
|
|
||||||
query_fragment["token_type"] = "bearer"
|
query_fragment["token_type"] = "bearer"
|
||||||
query_fragment["expires_in"] = timedelta_from_string(
|
query_fragment["expires_in"] = int(
|
||||||
self.provider.token_validity
|
timedelta_from_string(self.provider.token_validity).total_seconds()
|
||||||
).seconds
|
)
|
||||||
query_fragment["state"] = self.params.state if self.params.state else ""
|
query_fragment["state"] = self.params.state if self.params.state else ""
|
||||||
|
|
||||||
return query_fragment
|
return query_fragment
|
||||||
|
@ -468,14 +468,14 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
||||||
# OpenID clients can specify a `prompt` parameter, and if its set to consent we
|
# OpenID clients can specify a `prompt` parameter, and if its set to consent we
|
||||||
# need to inject a consent stage
|
# need to inject a consent stage
|
||||||
if PROMPT_CONSNET in self.params.prompt:
|
if PROMPT_CONSNET in self.params.prompt:
|
||||||
if not any(isinstance(x, ConsentStageView) for x in plan.stages):
|
if not any(isinstance(x.stage, ConsentStageView) for x in plan.bindings):
|
||||||
# Plan does not have any consent stage, so we add an in-memory one
|
# Plan does not have any consent stage, so we add an in-memory one
|
||||||
stage = ConsentStage(
|
stage = ConsentStage(
|
||||||
name="OAuth2 Provider In-memory consent stage",
|
name="OAuth2 Provider In-memory consent stage",
|
||||||
mode=ConsentMode.ALWAYS_REQUIRE,
|
mode=ConsentMode.ALWAYS_REQUIRE,
|
||||||
)
|
)
|
||||||
plan.append(stage)
|
plan.append_stage(stage)
|
||||||
plan.append(in_memory_stage(OAuthFulfillmentStage))
|
plan.append_stage(in_memory_stage(OAuthFulfillmentStage))
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -215,9 +215,11 @@ class TokenView(View):
|
||||||
"access_token": refresh_token.access_token,
|
"access_token": refresh_token.access_token,
|
||||||
"refresh_token": refresh_token.refresh_token,
|
"refresh_token": refresh_token.refresh_token,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
"expires_in": timedelta_from_string(
|
"expires_in": int(
|
||||||
self.params.provider.token_validity
|
timedelta_from_string(
|
||||||
).seconds,
|
self.params.provider.token_validity
|
||||||
|
).total_seconds()
|
||||||
|
),
|
||||||
"id_token": refresh_token.provider.encode(refresh_token.id_token.to_dict()),
|
"id_token": refresh_token.provider.encode(refresh_token.id_token.to_dict()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,9 +260,11 @@ class TokenView(View):
|
||||||
"access_token": refresh_token.access_token,
|
"access_token": refresh_token.access_token,
|
||||||
"refresh_token": refresh_token.refresh_token,
|
"refresh_token": refresh_token.refresh_token,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
"expires_in": timedelta_from_string(
|
"expires_in": int(
|
||||||
refresh_token.provider.token_validity
|
timedelta_from_string(
|
||||||
).seconds,
|
refresh_token.provider.token_validity
|
||||||
|
).total_seconds()
|
||||||
|
),
|
||||||
"id_token": self.params.provider.encode(refresh_token.id_token.to_dict()),
|
"id_token": self.params.provider.encode(refresh_token.id_token.to_dict()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ class SAMLSSOView(PolicyAccessView):
|
||||||
PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
|
PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
plan.append(in_memory_stage(SAMLFlowFinalView))
|
plan.append_stage(in_memory_stage(SAMLFlowFinalView))
|
||||||
request.session[SESSION_KEY_PLAN] = plan
|
request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -153,6 +153,7 @@ SPECTACULAR_SETTINGS = {
|
||||||
"url": "https://github.com/goauthentik/authentik/blob/master/LICENSE",
|
"url": "https://github.com/goauthentik/authentik/blob/master/LICENSE",
|
||||||
},
|
},
|
||||||
"ENUM_NAME_OVERRIDES": {
|
"ENUM_NAME_OVERRIDES": {
|
||||||
|
"EventActions": "authentik.events.models.EventAction",
|
||||||
"ChallengeChoices": "authentik.flows.challenge.ChallengeTypes",
|
"ChallengeChoices": "authentik.flows.challenge.ChallengeTypes",
|
||||||
"FlowDesignationEnum": "authentik.flows.models.FlowDesignation",
|
"FlowDesignationEnum": "authentik.flows.models.FlowDesignation",
|
||||||
"PolicyEngineMode": "authentik.policies.models.PolicyEngineMode",
|
"PolicyEngineMode": "authentik.policies.models.PolicyEngineMode",
|
||||||
|
|
|
@ -90,7 +90,7 @@ class InitiateView(View):
|
||||||
planner.allow_empty_flows = True
|
planner.allow_empty_flows = True
|
||||||
plan = planner.plan(self.request, kwargs)
|
plan = planner.plan(self.request, kwargs)
|
||||||
for stage in stages_to_append:
|
for stage in stages_to_append:
|
||||||
plan.append(stage)
|
plan.append_stage(stage)
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
"authentik_core:if-flow",
|
"authentik_core:if-flow",
|
||||||
|
|
|
@ -63,7 +63,7 @@ class AuthenticatorDuoStageView(ChallengeStageView):
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
"activation_barcode": enroll["activation_barcode"],
|
"activation_barcode": enroll["activation_barcode"],
|
||||||
"activation_code": enroll["activation_code"],
|
"activation_code": enroll["activation_code"],
|
||||||
"stage_uuid": stage.stage_uuid,
|
"stage_uuid": str(stage.stage_uuid),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
||||||
stage = Stage.objects.get_subclass(pk=stage.configuration_stage.pk)
|
stage = Stage.objects.get_subclass(pk=stage.configuration_stage.pk)
|
||||||
# plan.insert inserts at 1 index, so when stage_ok pops 0,
|
# plan.insert inserts at 1 index, so when stage_ok pops 0,
|
||||||
# the configuration stage is next
|
# the configuration stage is next
|
||||||
self.executor.plan.insert(stage)
|
self.executor.plan.insert_stage(stage)
|
||||||
return self.executor.stage_ok()
|
return self.executor.stage_ok()
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,14 @@ class TestCaptchaStage(TestCase):
|
||||||
public_key=RECAPTCHA_PUBLIC_KEY,
|
public_key=RECAPTCHA_PUBLIC_KEY,
|
||||||
private_key=RECAPTCHA_PRIVATE_KEY,
|
private_key=RECAPTCHA_PRIVATE_KEY,
|
||||||
)
|
)
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
def test_valid(self):
|
def test_valid(self):
|
||||||
"""Test valid captcha"""
|
"""Test valid captcha"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
|
|
@ -39,9 +39,11 @@ class TestConsentStage(TestCase):
|
||||||
stage = ConsentStage.objects.create(
|
stage = ConsentStage.objects.create(
|
||||||
name="consent", mode=ConsentMode.ALWAYS_REQUIRE
|
name="consent", mode=ConsentMode.ALWAYS_REQUIRE
|
||||||
)
|
)
|
||||||
FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
binding = FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
||||||
|
|
||||||
plan = FlowPlan(flow_pk=flow.pk.hex, stages=[stage], markers=[StageMarker()])
|
plan = FlowPlan(
|
||||||
|
flow_pk=flow.pk.hex, bindings=[binding], markers=[StageMarker()]
|
||||||
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
session.save()
|
session.save()
|
||||||
|
@ -69,11 +71,11 @@ class TestConsentStage(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
stage = ConsentStage.objects.create(name="consent", mode=ConsentMode.PERMANENT)
|
stage = ConsentStage.objects.create(name="consent", mode=ConsentMode.PERMANENT)
|
||||||
FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
binding = FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
||||||
|
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=flow.pk.hex,
|
flow_pk=flow.pk.hex,
|
||||||
stages=[stage],
|
bindings=[binding],
|
||||||
markers=[StageMarker()],
|
markers=[StageMarker()],
|
||||||
context={PLAN_CONTEXT_APPLICATION: self.application},
|
context={PLAN_CONTEXT_APPLICATION: self.application},
|
||||||
)
|
)
|
||||||
|
@ -110,11 +112,11 @@ class TestConsentStage(TestCase):
|
||||||
stage = ConsentStage.objects.create(
|
stage = ConsentStage.objects.create(
|
||||||
name="consent", mode=ConsentMode.EXPIRING, consent_expire_in="seconds=1"
|
name="consent", mode=ConsentMode.EXPIRING, consent_expire_in="seconds=1"
|
||||||
)
|
)
|
||||||
FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
binding = FlowStageBinding.objects.create(target=flow, stage=stage, order=2)
|
||||||
|
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=flow.pk.hex,
|
flow_pk=flow.pk.hex,
|
||||||
stages=[stage],
|
bindings=[binding],
|
||||||
markers=[StageMarker()],
|
markers=[StageMarker()],
|
||||||
context={PLAN_CONTEXT_APPLICATION: self.application},
|
context={PLAN_CONTEXT_APPLICATION: self.application},
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,12 +26,14 @@ class TestUserDenyStage(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
self.stage = DenyStage.objects.create(name="logout")
|
self.stage = DenyStage.objects.create(name="logout")
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
def test_valid_password(self):
|
def test_valid_password(self):
|
||||||
"""Test with a valid pending user and backend"""
|
"""Test with a valid pending user and backend"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
|
|
@ -34,12 +34,14 @@ class TestEmailStageSending(TestCase):
|
||||||
self.stage = EmailStage.objects.create(
|
self.stage = EmailStage.objects.create(
|
||||||
name="email",
|
name="email",
|
||||||
)
|
)
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
def test_pending_user(self):
|
def test_pending_user(self):
|
||||||
"""Test with pending user"""
|
"""Test with pending user"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -67,7 +69,7 @@ class TestEmailStageSending(TestCase):
|
||||||
def test_send_error(self):
|
def test_send_error(self):
|
||||||
"""Test error during sending (sending will be retried)"""
|
"""Test error during sending (sending will be retried)"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
|
|
@ -35,12 +35,14 @@ class TestEmailStage(TestCase):
|
||||||
self.stage = EmailStage.objects.create(
|
self.stage = EmailStage.objects.create(
|
||||||
name="email",
|
name="email",
|
||||||
)
|
)
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
def test_rendering(self):
|
def test_rendering(self):
|
||||||
"""Test with pending user"""
|
"""Test with pending user"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -56,7 +58,7 @@ class TestEmailStage(TestCase):
|
||||||
def test_without_user(self):
|
def test_without_user(self):
|
||||||
"""Test without pending user"""
|
"""Test without pending user"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -71,7 +73,7 @@ class TestEmailStage(TestCase):
|
||||||
def test_pending_user(self):
|
def test_pending_user(self):
|
||||||
"""Test with pending user"""
|
"""Test with pending user"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -102,7 +104,7 @@ class TestEmailStage(TestCase):
|
||||||
# Make sure token exists
|
# Make sure token exists
|
||||||
self.test_pending_user()
|
self.test_pending_user()
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
|
|
@ -85,6 +85,18 @@ class IdentificationChallengeResponse(ChallengeResponse):
|
||||||
identification_failed.send(
|
identification_failed.send(
|
||||||
sender=self, request=self.stage.request, uid_field=uid_field
|
sender=self, request=self.stage.request, uid_field=uid_field
|
||||||
)
|
)
|
||||||
|
# We set the pending_user even on failure so it's part of the context, even
|
||||||
|
# when the input is invalid
|
||||||
|
# This is so its part of the current flow plan, and on flow restart can be kept, and
|
||||||
|
# policies can be applied.
|
||||||
|
self.stage.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(
|
||||||
|
username=uid_field,
|
||||||
|
email=uid_field,
|
||||||
|
)
|
||||||
|
if not current_stage.show_matched_user:
|
||||||
|
self.stage.executor.plan.context[
|
||||||
|
PLAN_CONTEXT_PENDING_USER_IDENTIFIER
|
||||||
|
] = uid_field
|
||||||
raise ValidationError("Failed to authenticate.")
|
raise ValidationError("Failed to authenticate.")
|
||||||
self.pre_user = pre_user
|
self.pre_user = pre_user
|
||||||
if not current_stage.password_stage:
|
if not current_stage.password_stage:
|
||||||
|
|
|
@ -35,7 +35,9 @@ class TestUserLoginStage(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
self.stage = InvitationStage.objects.create(name="invitation")
|
self.stage = InvitationStage.objects.create(name="invitation")
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"authentik.flows.views.to_stage_response",
|
"authentik.flows.views.to_stage_response",
|
||||||
|
@ -44,7 +46,7 @@ class TestUserLoginStage(TestCase):
|
||||||
def test_without_invitation_fail(self):
|
def test_without_invitation_fail(self):
|
||||||
"""Test without any invitation, continue_flow_without_invitation not set."""
|
"""Test without any invitation, continue_flow_without_invitation not set."""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
|
plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
|
||||||
|
@ -75,7 +77,7 @@ class TestUserLoginStage(TestCase):
|
||||||
self.stage.continue_flow_without_invitation = True
|
self.stage.continue_flow_without_invitation = True
|
||||||
self.stage.save()
|
self.stage.save()
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
|
plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
|
||||||
|
@ -103,7 +105,7 @@ class TestUserLoginStage(TestCase):
|
||||||
def test_with_invitation_get(self):
|
def test_with_invitation_get(self):
|
||||||
"""Test with invitation, check data in session"""
|
"""Test with invitation, check data in session"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -143,7 +145,7 @@ class TestUserLoginStage(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PROMPT] = {INVITATION_TOKEN_KEY: invite.pk.hex}
|
plan.context[PLAN_CONTEXT_PROMPT] = {INVITATION_TOKEN_KEY: invite.pk.hex}
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
|
|
@ -39,7 +39,9 @@ class TestPasswordStage(TestCase):
|
||||||
self.stage = PasswordStage.objects.create(
|
self.stage = PasswordStage.objects.create(
|
||||||
name="password", backends=[BACKEND_DJANGO]
|
name="password", backends=[BACKEND_DJANGO]
|
||||||
)
|
)
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"authentik.flows.views.to_stage_response",
|
"authentik.flows.views.to_stage_response",
|
||||||
|
@ -48,7 +50,7 @@ class TestPasswordStage(TestCase):
|
||||||
def test_without_user(self):
|
def test_without_user(self):
|
||||||
"""Test without user"""
|
"""Test without user"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -84,7 +86,7 @@ class TestPasswordStage(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -101,7 +103,7 @@ class TestPasswordStage(TestCase):
|
||||||
def test_valid_password(self):
|
def test_valid_password(self):
|
||||||
"""Test with a valid pending user and valid password"""
|
"""Test with a valid pending user and valid password"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -129,7 +131,7 @@ class TestPasswordStage(TestCase):
|
||||||
def test_invalid_password(self):
|
def test_invalid_password(self):
|
||||||
"""Test with a valid pending user and invalid password"""
|
"""Test with a valid pending user and invalid password"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -148,7 +150,7 @@ class TestPasswordStage(TestCase):
|
||||||
def test_invalid_password_lockout(self):
|
def test_invalid_password_lockout(self):
|
||||||
"""Test with a valid pending user and invalid password (trigger logout counter)"""
|
"""Test with a valid pending user and invalid password (trigger logout counter)"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -189,7 +191,7 @@ class TestPasswordStage(TestCase):
|
||||||
"""Test with a valid pending user and valid password.
|
"""Test with a valid pending user and valid password.
|
||||||
Backend is patched to return PermissionError"""
|
Backend is patched to return PermissionError"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
|
|
@ -90,6 +90,14 @@ class PromptChallengeResponse(ChallengeResponse):
|
||||||
raise ValidationError(_("Passwords don't match."))
|
raise ValidationError(_("Passwords don't match."))
|
||||||
|
|
||||||
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
# Check if we have any static or hidden fields, and ensure they
|
||||||
|
# still have the same value
|
||||||
|
static_hidden_fields: QuerySet[Prompt] = self.stage.fields.filter(
|
||||||
|
type__in=[FieldTypes.HIDDEN, FieldTypes.STATIC]
|
||||||
|
)
|
||||||
|
for static_hidden in static_hidden_fields:
|
||||||
|
attrs[static_hidden.field_key] = static_hidden.placeholder
|
||||||
|
|
||||||
# Check if we have two password fields, and make sure they are the same
|
# Check if we have two password fields, and make sure they are the same
|
||||||
password_fields: QuerySet[Prompt] = self.stage.fields.filter(
|
password_fields: QuerySet[Prompt] = self.stage.fields.filter(
|
||||||
type=FieldTypes.PASSWORD
|
type=FieldTypes.PASSWORD
|
||||||
|
|
|
@ -78,6 +78,12 @@ class TestPromptStage(TestCase):
|
||||||
required=True,
|
required=True,
|
||||||
placeholder="HIDDEN_PLACEHOLDER",
|
placeholder="HIDDEN_PLACEHOLDER",
|
||||||
)
|
)
|
||||||
|
static_prompt = Prompt.objects.create(
|
||||||
|
field_key="static_prompt",
|
||||||
|
type=FieldTypes.STATIC,
|
||||||
|
required=True,
|
||||||
|
placeholder="static",
|
||||||
|
)
|
||||||
self.stage = PromptStage.objects.create(name="prompt-stage")
|
self.stage = PromptStage.objects.create(name="prompt-stage")
|
||||||
self.stage.fields.set(
|
self.stage.fields.set(
|
||||||
[
|
[
|
||||||
|
@ -88,6 +94,7 @@ class TestPromptStage(TestCase):
|
||||||
password2_prompt,
|
password2_prompt,
|
||||||
number_prompt,
|
number_prompt,
|
||||||
hidden_prompt,
|
hidden_prompt,
|
||||||
|
static_prompt,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.stage.save()
|
self.stage.save()
|
||||||
|
@ -100,14 +107,17 @@ class TestPromptStage(TestCase):
|
||||||
password2_prompt.field_key: "test",
|
password2_prompt.field_key: "test",
|
||||||
number_prompt.field_key: 3,
|
number_prompt.field_key: 3,
|
||||||
hidden_prompt.field_key: hidden_prompt.placeholder,
|
hidden_prompt.field_key: hidden_prompt.placeholder,
|
||||||
|
static_prompt.field_key: static_prompt.placeholder,
|
||||||
}
|
}
|
||||||
|
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
def test_render(self):
|
def test_render(self):
|
||||||
"""Test render of form, check if all prompts are rendered correctly"""
|
"""Test render of form, check if all prompts are rendered correctly"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -125,7 +135,7 @@ class TestPromptStage(TestCase):
|
||||||
def test_valid_challenge_with_policy(self) -> PromptChallengeResponse:
|
def test_valid_challenge_with_policy(self) -> PromptChallengeResponse:
|
||||||
"""Test challenge_response validation"""
|
"""Test challenge_response validation"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
expr = "return request.context['password_prompt'] == request.context['password2_prompt']"
|
expr = "return request.context['password_prompt'] == request.context['password2_prompt']"
|
||||||
expr_policy = ExpressionPolicy.objects.create(
|
expr_policy = ExpressionPolicy.objects.create(
|
||||||
|
@ -142,7 +152,7 @@ class TestPromptStage(TestCase):
|
||||||
def test_invalid_challenge(self) -> PromptChallengeResponse:
|
def test_invalid_challenge(self) -> PromptChallengeResponse:
|
||||||
"""Test challenge_response validation"""
|
"""Test challenge_response validation"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
expr = "False"
|
expr = "False"
|
||||||
expr_policy = ExpressionPolicy.objects.create(
|
expr_policy = ExpressionPolicy.objects.create(
|
||||||
|
@ -159,7 +169,7 @@ class TestPromptStage(TestCase):
|
||||||
def test_valid_challenge_request(self):
|
def test_valid_challenge_request(self):
|
||||||
"""Test a request with valid challenge_response data"""
|
"""Test a request with valid challenge_response data"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -196,7 +206,7 @@ class TestPromptStage(TestCase):
|
||||||
def test_invalid_password(self):
|
def test_invalid_password(self):
|
||||||
"""Test challenge_response validation"""
|
"""Test challenge_response validation"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
self.prompt_data["password2_prompt"] = "qwerqwerqr"
|
self.prompt_data["password2_prompt"] = "qwerqwerqr"
|
||||||
challenge_response = PromptChallengeResponse(
|
challenge_response = PromptChallengeResponse(
|
||||||
|
@ -215,7 +225,7 @@ class TestPromptStage(TestCase):
|
||||||
def test_invalid_username(self):
|
def test_invalid_username(self):
|
||||||
"""Test challenge_response validation"""
|
"""Test challenge_response validation"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
self.prompt_data["username_prompt"] = "akadmin"
|
self.prompt_data["username_prompt"] = "akadmin"
|
||||||
challenge_response = PromptChallengeResponse(
|
challenge_response = PromptChallengeResponse(
|
||||||
|
@ -230,3 +240,17 @@ class TestPromptStage(TestCase):
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_static_hidden_overwrite(self):
|
||||||
|
"""Test that static and hidden fields ignore any value sent to them"""
|
||||||
|
plan = FlowPlan(
|
||||||
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
|
)
|
||||||
|
self.prompt_data["hidden_prompt"] = "foo"
|
||||||
|
self.prompt_data["static_prompt"] = "foo"
|
||||||
|
challenge_response = PromptChallengeResponse(
|
||||||
|
None, stage=self.stage, plan=plan, data=self.prompt_data
|
||||||
|
)
|
||||||
|
self.assertEqual(challenge_response.is_valid(), True)
|
||||||
|
self.assertNotEqual(challenge_response.validated_data["hidden_prompt"], "foo")
|
||||||
|
self.assertNotEqual(challenge_response.validated_data["static_prompt"], "foo")
|
||||||
|
|
|
@ -30,7 +30,9 @@ class TestUserDeleteStage(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
self.stage = UserDeleteStage.objects.create(name="delete")
|
self.stage = UserDeleteStage.objects.create(name="delete")
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"authentik.flows.views.to_stage_response",
|
"authentik.flows.views.to_stage_response",
|
||||||
|
@ -39,7 +41,7 @@ class TestUserDeleteStage(TestCase):
|
||||||
def test_no_user(self):
|
def test_no_user(self):
|
||||||
"""Test without user set"""
|
"""Test without user set"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -66,7 +68,7 @@ class TestUserDeleteStage(TestCase):
|
||||||
def test_user_delete_get(self):
|
def test_user_delete_get(self):
|
||||||
"""Test Form render"""
|
"""Test Form render"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
|
|
@ -30,12 +30,14 @@ class TestUserLoginStage(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
self.stage = UserLoginStage.objects.create(name="login")
|
self.stage = UserLoginStage.objects.create(name="login")
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
def test_valid_password(self):
|
def test_valid_password(self):
|
||||||
"""Test with a valid pending user and backend"""
|
"""Test with a valid pending user and backend"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -61,7 +63,7 @@ class TestUserLoginStage(TestCase):
|
||||||
self.stage.session_duration = "seconds=2"
|
self.stage.session_duration = "seconds=2"
|
||||||
self.stage.save()
|
self.stage.save()
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
|
@ -92,7 +94,7 @@ class TestUserLoginStage(TestCase):
|
||||||
def test_without_user(self):
|
def test_without_user(self):
|
||||||
"""Test a plan without any pending user, resulting in a denied"""
|
"""Test a plan without any pending user, resulting in a denied"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
|
|
@ -28,12 +28,14 @@ class TestUserLogoutStage(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
self.stage = UserLogoutStage.objects.create(name="logout")
|
self.stage = UserLogoutStage.objects.create(name="logout")
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
|
|
||||||
def test_valid_password(self):
|
def test_valid_password(self):
|
||||||
"""Test with a valid pending user and backend"""
|
"""Test with a valid pending user and backend"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
|
plan.context[PLAN_CONTEXT_AUTHENTICATION_BACKEND] = BACKEND_DJANGO
|
||||||
|
|
|
@ -12,7 +12,7 @@ class UserWriteStageSerializer(StageSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
model = UserWriteStage
|
model = UserWriteStage
|
||||||
fields = StageSerializer.Meta.fields
|
fields = StageSerializer.Meta.fields + ["create_users_as_inactive"]
|
||||||
|
|
||||||
|
|
||||||
class UserWriteStageViewSet(UsedByMixin, ModelViewSet):
|
class UserWriteStageViewSet(UsedByMixin, ModelViewSet):
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-06-28 20:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_user_write", "0002_auto_20200918_1653"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="userwritestage",
|
||||||
|
name="create_users_as_inactive",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="When set, newly created users are inactive and cannot login.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,6 +1,7 @@
|
||||||
"""write stage models"""
|
"""write stage models"""
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
@ -12,6 +13,11 @@ class UserWriteStage(Stage):
|
||||||
"""Writes currently pending data into the pending user, or if no user exists,
|
"""Writes currently pending data into the pending user, or if no user exists,
|
||||||
creates a new user with the data."""
|
creates a new user with the data."""
|
||||||
|
|
||||||
|
create_users_as_inactive = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text=_("When set, newly created users are inactive and cannot login."),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> BaseSerializer:
|
def serializer(self) -> BaseSerializer:
|
||||||
from authentik.stages.user_write.api import UserWriteStageSerializer
|
from authentik.stages.user_write.api import UserWriteStageSerializer
|
||||||
|
|
|
@ -35,7 +35,9 @@ class UserWriteStageView(StageView):
|
||||||
data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]
|
data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]
|
||||||
user_created = False
|
user_created = False
|
||||||
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||||
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User()
|
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(
|
||||||
|
is_active=not self.executor.current_stage.create_users_as_inactive
|
||||||
|
)
|
||||||
self.executor.plan.context[
|
self.executor.plan.context[
|
||||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||||
] = class_to_path(ModelBackend)
|
] = class_to_path(ModelBackend)
|
||||||
|
|
|
@ -37,7 +37,9 @@ class TestUserWriteStage(TestCase):
|
||||||
designation=FlowDesignation.AUTHENTICATION,
|
designation=FlowDesignation.AUTHENTICATION,
|
||||||
)
|
)
|
||||||
self.stage = UserWriteStage.objects.create(name="write")
|
self.stage = UserWriteStage.objects.create(name="write")
|
||||||
FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
self.binding = FlowStageBinding.objects.create(
|
||||||
|
target=self.flow, stage=self.stage, order=2
|
||||||
|
)
|
||||||
self.source = Source.objects.create(name="fake_source")
|
self.source = Source.objects.create(name="fake_source")
|
||||||
|
|
||||||
def test_user_create(self):
|
def test_user_create(self):
|
||||||
|
@ -48,7 +50,7 @@ class TestUserWriteStage(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PROMPT] = {
|
plan.context[PLAN_CONTEXT_PROMPT] = {
|
||||||
"username": "test-user",
|
"username": "test-user",
|
||||||
|
@ -92,7 +94,7 @@ class TestUserWriteStage(TestCase):
|
||||||
for _ in range(8)
|
for _ in range(8)
|
||||||
)
|
)
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
plan.context[PLAN_CONTEXT_PENDING_USER] = User.objects.create(
|
plan.context[PLAN_CONTEXT_PENDING_USER] = User.objects.create(
|
||||||
username="unittest", email="test@beryju.org"
|
username="unittest", email="test@beryju.org"
|
||||||
|
@ -135,7 +137,7 @@ class TestUserWriteStage(TestCase):
|
||||||
def test_without_data(self):
|
def test_without_data(self):
|
||||||
"""Test without data results in error"""
|
"""Test without data results in error"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session[SESSION_KEY_PLAN] = plan
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
@ -167,7 +169,7 @@ class TestUserWriteStage(TestCase):
|
||||||
def test_blank_username(self):
|
def test_blank_username(self):
|
||||||
"""Test with blank username results in error"""
|
"""Test with blank username results in error"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
plan.context[PLAN_CONTEXT_PROMPT] = {
|
plan.context[PLAN_CONTEXT_PROMPT] = {
|
||||||
|
@ -204,7 +206,7 @@ class TestUserWriteStage(TestCase):
|
||||||
def test_duplicate_data(self):
|
def test_duplicate_data(self):
|
||||||
"""Test with duplicate data, should trigger error"""
|
"""Test with duplicate data, should trigger error"""
|
||||||
plan = FlowPlan(
|
plan = FlowPlan(
|
||||||
flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
)
|
)
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
plan.context[PLAN_CONTEXT_PROMPT] = {
|
plan.context[PLAN_CONTEXT_PROMPT] = {
|
||||||
|
|
|
@ -54,6 +54,9 @@ class CurrentTenantSerializer(PassiveSerializer):
|
||||||
default=CONFIG.y("footer_links", []),
|
default=CONFIG.y("footer_links", []),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
flow_authentication = CharField(source="flow_authentication.slug", required=False)
|
||||||
|
flow_invalidation = CharField(source="flow_invalidation.slug", required=False)
|
||||||
|
flow_recovery = CharField(source="flow_recovery.slug", required=False)
|
||||||
flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
|
flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ class TestTenants(TestCase):
|
||||||
"branding_title": "authentik",
|
"branding_title": "authentik",
|
||||||
"matched_domain": "authentik-default",
|
"matched_domain": "authentik-default",
|
||||||
"ui_footer_links": CONFIG.y("footer_links"),
|
"ui_footer_links": CONFIG.y("footer_links"),
|
||||||
|
"flow_authentication": "default-authentication-flow",
|
||||||
|
"flow_invalidation": "default-invalidation-flow",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -99,15 +99,15 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *u.IsActive {
|
if *u.IsActive {
|
||||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"inactive"}})
|
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"active"}})
|
||||||
} else {
|
} else {
|
||||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"active"}})
|
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"inactive"}})
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.IsSuperuser {
|
if u.IsSuperuser {
|
||||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "superuser", Values: []string{"inactive"}})
|
|
||||||
} else {
|
|
||||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "superuser", Values: []string{"active"}})
|
attrs = append(attrs, &ldap.EntryAttribute{Name: "superuser", Values: []string{"active"}})
|
||||||
|
} else {
|
||||||
|
attrs = append(attrs, &ldap.EntryAttribute{Name: "superuser", Values: []string{"inactive"}})
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: pi.GroupsForUser(u)})
|
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: pi.GroupsForUser(u)})
|
||||||
|
|
274
schema.yml
274
schema.yml
|
@ -3572,6 +3572,37 @@ paths:
|
||||||
$ref: '#/components/schemas/ValidationError'
|
$ref: '#/components/schemas/ValidationError'
|
||||||
'403':
|
'403':
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
post:
|
||||||
|
operationId: events_events_create
|
||||||
|
description: Event Read-Only Viewset
|
||||||
|
tags:
|
||||||
|
- events
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/EventRequest'
|
||||||
|
application/x-www-form-urlencoded:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/EventRequest'
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/EventRequest'
|
||||||
|
required: true
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
- cookieAuth: []
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Event'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
/api/v2beta/events/events/{event_uuid}/:
|
/api/v2beta/events/events/{event_uuid}/:
|
||||||
get:
|
get:
|
||||||
operationId: events_events_retrieve
|
operationId: events_events_retrieve
|
||||||
|
@ -3600,6 +3631,106 @@ paths:
|
||||||
$ref: '#/components/schemas/ValidationError'
|
$ref: '#/components/schemas/ValidationError'
|
||||||
'403':
|
'403':
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
put:
|
||||||
|
operationId: events_events_update
|
||||||
|
description: Event Read-Only Viewset
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: event_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this Event.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- events
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/EventRequest'
|
||||||
|
application/x-www-form-urlencoded:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/EventRequest'
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/EventRequest'
|
||||||
|
required: true
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
- cookieAuth: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Event'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
patch:
|
||||||
|
operationId: events_events_partial_update
|
||||||
|
description: Event Read-Only Viewset
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: event_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this Event.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- events
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PatchedEventRequest'
|
||||||
|
application/x-www-form-urlencoded:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PatchedEventRequest'
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PatchedEventRequest'
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
- cookieAuth: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Event'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
delete:
|
||||||
|
operationId: events_events_destroy
|
||||||
|
description: Event Read-Only Viewset
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: event_uuid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: A UUID string identifying this Event.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- events
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
- cookieAuth: []
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: No response body
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
'403':
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
/api/v2beta/events/events/actions/:
|
/api/v2beta/events/events/actions/:
|
||||||
get:
|
get:
|
||||||
operationId: events_events_actions_list
|
operationId: events_events_actions_list
|
||||||
|
@ -4441,6 +4572,18 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
- in: query
|
||||||
|
name: invalid_response_action
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- restart
|
||||||
|
- restart_with_context
|
||||||
|
- retry
|
||||||
|
description: Configure how the flow executor should handle an invalid response
|
||||||
|
to a challenge. RETRY returns the error message and a similar challenge
|
||||||
|
to the executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT
|
||||||
|
restarts the flow while keeping the current context.
|
||||||
- in: query
|
- in: query
|
||||||
name: order
|
name: order
|
||||||
schema:
|
schema:
|
||||||
|
@ -18759,6 +18902,12 @@ components:
|
||||||
name: Documentation
|
name: Documentation
|
||||||
- href: https://goauthentik.io/
|
- href: https://goauthentik.io/
|
||||||
name: authentik Website
|
name: authentik Website
|
||||||
|
flow_authentication:
|
||||||
|
type: string
|
||||||
|
flow_invalidation:
|
||||||
|
type: string
|
||||||
|
flow_recovery:
|
||||||
|
type: string
|
||||||
flow_unenrollment:
|
flow_unenrollment:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
|
@ -19242,7 +19391,7 @@ components:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
action:
|
action:
|
||||||
type: string
|
$ref: '#/components/schemas/EventActions'
|
||||||
app:
|
app:
|
||||||
type: string
|
type: string
|
||||||
context:
|
context:
|
||||||
|
@ -19266,6 +19415,34 @@ components:
|
||||||
- app
|
- app
|
||||||
- created
|
- created
|
||||||
- pk
|
- pk
|
||||||
|
EventActions:
|
||||||
|
enum:
|
||||||
|
- login
|
||||||
|
- login_failed
|
||||||
|
- logout
|
||||||
|
- user_write
|
||||||
|
- suspicious_request
|
||||||
|
- password_set
|
||||||
|
- secret_view
|
||||||
|
- invitation_used
|
||||||
|
- authorize_application
|
||||||
|
- source_linked
|
||||||
|
- impersonation_started
|
||||||
|
- impersonation_ended
|
||||||
|
- policy_execution
|
||||||
|
- policy_exception
|
||||||
|
- property_mapping_exception
|
||||||
|
- system_task_execution
|
||||||
|
- system_task_exception
|
||||||
|
- system_exception
|
||||||
|
- configuration_error
|
||||||
|
- model_created
|
||||||
|
- model_updated
|
||||||
|
- model_deleted
|
||||||
|
- email_sent
|
||||||
|
- update_available
|
||||||
|
- custom_
|
||||||
|
type: string
|
||||||
EventMatcherPolicy:
|
EventMatcherPolicy:
|
||||||
type: object
|
type: object
|
||||||
description: Event Matcher Policy Serializer
|
description: Event Matcher Policy Serializer
|
||||||
|
@ -19296,7 +19473,7 @@ components:
|
||||||
readOnly: true
|
readOnly: true
|
||||||
action:
|
action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/EventMatcherPolicyActionEnum'
|
- $ref: '#/components/schemas/EventActions'
|
||||||
description: Match created events with this action type. When left empty,
|
description: Match created events with this action type. When left empty,
|
||||||
all action types will be matched.
|
all action types will be matched.
|
||||||
client_ip:
|
client_ip:
|
||||||
|
@ -19314,34 +19491,6 @@ components:
|
||||||
- pk
|
- pk
|
||||||
- verbose_name
|
- verbose_name
|
||||||
- verbose_name_plural
|
- verbose_name_plural
|
||||||
EventMatcherPolicyActionEnum:
|
|
||||||
enum:
|
|
||||||
- login
|
|
||||||
- login_failed
|
|
||||||
- logout
|
|
||||||
- user_write
|
|
||||||
- suspicious_request
|
|
||||||
- password_set
|
|
||||||
- secret_view
|
|
||||||
- invitation_used
|
|
||||||
- authorize_application
|
|
||||||
- source_linked
|
|
||||||
- impersonation_started
|
|
||||||
- impersonation_ended
|
|
||||||
- policy_execution
|
|
||||||
- policy_exception
|
|
||||||
- property_mapping_exception
|
|
||||||
- system_task_execution
|
|
||||||
- system_task_exception
|
|
||||||
- system_exception
|
|
||||||
- configuration_error
|
|
||||||
- model_created
|
|
||||||
- model_updated
|
|
||||||
- model_deleted
|
|
||||||
- email_sent
|
|
||||||
- update_available
|
|
||||||
- custom_
|
|
||||||
type: string
|
|
||||||
EventMatcherPolicyRequest:
|
EventMatcherPolicyRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Event Matcher Policy Serializer
|
description: Event Matcher Policy Serializer
|
||||||
|
@ -19355,7 +19504,7 @@ components:
|
||||||
will be logged. By default, only execution errors are logged.
|
will be logged. By default, only execution errors are logged.
|
||||||
action:
|
action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/EventMatcherPolicyActionEnum'
|
- $ref: '#/components/schemas/EventActions'
|
||||||
description: Match created events with this action type. When left empty,
|
description: Match created events with this action type. When left empty,
|
||||||
all action types will be matched.
|
all action types will be matched.
|
||||||
client_ip:
|
client_ip:
|
||||||
|
@ -19375,7 +19524,7 @@ components:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
action:
|
action:
|
||||||
type: string
|
$ref: '#/components/schemas/EventActions'
|
||||||
app:
|
app:
|
||||||
type: string
|
type: string
|
||||||
context:
|
context:
|
||||||
|
@ -19673,6 +19822,13 @@ components:
|
||||||
minimum: -2147483648
|
minimum: -2147483648
|
||||||
policy_engine_mode:
|
policy_engine_mode:
|
||||||
$ref: '#/components/schemas/PolicyEngineMode'
|
$ref: '#/components/schemas/PolicyEngineMode'
|
||||||
|
invalid_response_action:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/InvalidResponseActionEnum'
|
||||||
|
description: Configure how the flow executor should handle an invalid response
|
||||||
|
to a challenge. RETRY returns the error message and a similar challenge
|
||||||
|
to the executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT
|
||||||
|
restarts the flow while keeping the current context.
|
||||||
required:
|
required:
|
||||||
- order
|
- order
|
||||||
- pk
|
- pk
|
||||||
|
@ -19703,6 +19859,13 @@ components:
|
||||||
minimum: -2147483648
|
minimum: -2147483648
|
||||||
policy_engine_mode:
|
policy_engine_mode:
|
||||||
$ref: '#/components/schemas/PolicyEngineMode'
|
$ref: '#/components/schemas/PolicyEngineMode'
|
||||||
|
invalid_response_action:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/InvalidResponseActionEnum'
|
||||||
|
description: Configure how the flow executor should handle an invalid response
|
||||||
|
to a challenge. RETRY returns the error message and a similar challenge
|
||||||
|
to the executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT
|
||||||
|
restarts the flow while keeping the current context.
|
||||||
required:
|
required:
|
||||||
- order
|
- order
|
||||||
- stage
|
- stage
|
||||||
|
@ -20048,6 +20211,12 @@ components:
|
||||||
- api
|
- api
|
||||||
- recovery
|
- recovery
|
||||||
type: string
|
type: string
|
||||||
|
InvalidResponseActionEnum:
|
||||||
|
enum:
|
||||||
|
- retry
|
||||||
|
- restart
|
||||||
|
- restart_with_context
|
||||||
|
type: string
|
||||||
Invitation:
|
Invitation:
|
||||||
type: object
|
type: object
|
||||||
description: Invitation Serializer
|
description: Invitation Serializer
|
||||||
|
@ -24445,7 +24614,7 @@ components:
|
||||||
will be logged. By default, only execution errors are logged.
|
will be logged. By default, only execution errors are logged.
|
||||||
action:
|
action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/EventMatcherPolicyActionEnum'
|
- $ref: '#/components/schemas/EventActions'
|
||||||
description: Match created events with this action type. When left empty,
|
description: Match created events with this action type. When left empty,
|
||||||
all action types will be matched.
|
all action types will be matched.
|
||||||
client_ip:
|
client_ip:
|
||||||
|
@ -24457,6 +24626,29 @@ components:
|
||||||
- $ref: '#/components/schemas/AppEnum'
|
- $ref: '#/components/schemas/AppEnum'
|
||||||
description: Match events created by selected application. When left empty,
|
description: Match events created by selected application. When left empty,
|
||||||
all applications are matched.
|
all applications are matched.
|
||||||
|
PatchedEventRequest:
|
||||||
|
type: object
|
||||||
|
description: Event Serializer
|
||||||
|
properties:
|
||||||
|
user:
|
||||||
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
|
action:
|
||||||
|
$ref: '#/components/schemas/EventActions'
|
||||||
|
app:
|
||||||
|
type: string
|
||||||
|
context:
|
||||||
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
|
client_ip:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
expires:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
tenant:
|
||||||
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
PatchedExpressionPolicyRequest:
|
PatchedExpressionPolicyRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Group Membership Policy Serializer
|
description: Group Membership Policy Serializer
|
||||||
|
@ -24518,6 +24710,13 @@ components:
|
||||||
minimum: -2147483648
|
minimum: -2147483648
|
||||||
policy_engine_mode:
|
policy_engine_mode:
|
||||||
$ref: '#/components/schemas/PolicyEngineMode'
|
$ref: '#/components/schemas/PolicyEngineMode'
|
||||||
|
invalid_response_action:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/InvalidResponseActionEnum'
|
||||||
|
description: Configure how the flow executor should handle an invalid response
|
||||||
|
to a challenge. RETRY returns the error message and a similar challenge
|
||||||
|
to the executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT
|
||||||
|
restarts the flow while keeping the current context.
|
||||||
PatchedGroupRequest:
|
PatchedGroupRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Group Serializer
|
description: Group Serializer
|
||||||
|
@ -25603,6 +25802,9 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/FlowRequest'
|
$ref: '#/components/schemas/FlowRequest'
|
||||||
|
create_users_as_inactive:
|
||||||
|
type: boolean
|
||||||
|
description: When set, newly created users are inactive and cannot login.
|
||||||
PatchedWebAuthnDeviceRequest:
|
PatchedWebAuthnDeviceRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Serializer for WebAuthn authenticator devices
|
description: Serializer for WebAuthn authenticator devices
|
||||||
|
@ -28097,6 +28299,9 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Flow'
|
$ref: '#/components/schemas/Flow'
|
||||||
|
create_users_as_inactive:
|
||||||
|
type: boolean
|
||||||
|
description: When set, newly created users are inactive and cannot login.
|
||||||
required:
|
required:
|
||||||
- component
|
- component
|
||||||
- name
|
- name
|
||||||
|
@ -28113,6 +28318,9 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/FlowRequest'
|
$ref: '#/components/schemas/FlowRequest'
|
||||||
|
create_users_as_inactive:
|
||||||
|
type: boolean
|
||||||
|
description: When set, newly created users are inactive and cannot login.
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
ValidationError:
|
ValidationError:
|
||||||
|
|
|
@ -18,24 +18,24 @@
|
||||||
"@lingui/cli": "^3.10.2",
|
"@lingui/cli": "^3.10.2",
|
||||||
"@lingui/core": "^3.10.4",
|
"@lingui/core": "^3.10.4",
|
||||||
"@lingui/macro": "^3.10.2",
|
"@lingui/macro": "^3.10.2",
|
||||||
"@patternfly/patternfly": "^4.108.2",
|
"@patternfly/patternfly": "^4.115.2",
|
||||||
"@polymer/iron-form": "^3.0.1",
|
"@polymer/iron-form": "^3.0.1",
|
||||||
"@polymer/paper-input": "^3.2.1",
|
"@polymer/paper-input": "^3.2.1",
|
||||||
"@rollup/plugin-babel": "^5.3.0",
|
"@rollup/plugin-babel": "^5.3.0",
|
||||||
"@rollup/plugin-replace": "^2.4.2",
|
"@rollup/plugin-replace": "^2.4.2",
|
||||||
"@rollup/plugin-typescript": "^8.2.1",
|
"@rollup/plugin-typescript": "^8.2.1",
|
||||||
"@sentry/browser": "^6.7.2",
|
"@sentry/browser": "^6.8.0",
|
||||||
"@sentry/tracing": "^6.7.2",
|
"@sentry/tracing": "^6.8.0",
|
||||||
"@types/chart.js": "^2.9.32",
|
"@types/chart.js": "^2.9.32",
|
||||||
"@types/codemirror": "5.60.0",
|
"@types/codemirror": "5.60.1",
|
||||||
"@types/grecaptcha": "^3.0.2",
|
"@types/grecaptcha": "^3.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
"@typescript-eslint/eslint-plugin": "^4.28.1",
|
||||||
"@typescript-eslint/parser": "^4.28.0",
|
"@typescript-eslint/parser": "^4.28.1",
|
||||||
"@webcomponents/webcomponentsjs": "^2.5.0",
|
"@webcomponents/webcomponentsjs": "^2.5.0",
|
||||||
"authentik-api": "file:api",
|
"authentik-api": "file:api",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"chart.js": "^3.3.2",
|
"chart.js": "^3.4.0",
|
||||||
"chartjs-adapter-moment": "^1.0.0",
|
"chartjs-adapter-moment": "^1.0.0",
|
||||||
"codemirror": "^5.62.0",
|
"codemirror": "^5.62.0",
|
||||||
"construct-style-sheets-polyfill": "^2.4.16",
|
"construct-style-sheets-polyfill": "^2.4.16",
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
"lit-html": "^1.4.1",
|
"lit-html": "^1.4.1",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"rapidoc": "^9.0.0",
|
"rapidoc": "^9.0.0",
|
||||||
"rollup": "^2.52.2",
|
"rollup": "^2.52.3",
|
||||||
"rollup-plugin-commonjs": "^10.1.0",
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
"rollup-plugin-copy": "^3.4.0",
|
"rollup-plugin-copy": "^3.4.0",
|
||||||
"rollup-plugin-cssimport": "^1.0.2",
|
"rollup-plugin-cssimport": "^1.0.2",
|
||||||
|
@ -2120,9 +2120,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@patternfly/patternfly": {
|
"node_modules/@patternfly/patternfly": {
|
||||||
"version": "4.108.2",
|
"version": "4.115.2",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.108.2.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.115.2.tgz",
|
||||||
"integrity": "sha512-z0VB+1CXcH+eoClYQABwapX5FURSvm1nPr6asLWwg/Z4Wuxs0RjZpC6Gb+KRm8nGQwSAcMKZY1jLfPqVnznQnw=="
|
"integrity": "sha512-7hbJ4pRmj+rlXclD2F/UwceO6fS+9flGsgHc4eUc7NyTN2GXl6PLcqrjE2CtiKEPV90+KwsGQGJXZj8bz9HweA=="
|
||||||
},
|
},
|
||||||
"node_modules/@polymer/font-roboto": {
|
"node_modules/@polymer/font-roboto": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@ -2314,13 +2314,13 @@
|
||||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
|
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/browser": {
|
"node_modules/@sentry/browser": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.8.0.tgz",
|
||||||
"integrity": "sha512-Lv0Ne1QcesyGAhVcQDfQa3hDPR/MhPSDTMg3xFi+LxqztchVc4w/ynzR0wCZFb8KIHpTj5SpJHfxpDhXYMtS9g==",
|
"integrity": "sha512-nxa71csHlG5sMHUxI4e4xxuCWtbCv/QbBfMsYw7ncJSfCKG3yNlCVh8NJ7NS0rZW/MJUT6S6+r93zw0HetNDOA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "6.7.2",
|
"@sentry/core": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2333,14 +2333,14 @@
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/core": {
|
"node_modules/@sentry/core": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.8.0.tgz",
|
||||||
"integrity": "sha512-NTZqwN5nR94yrXmSfekoPs1mIFuKvf8esdIW/DadwSKWAdLJwQTJY9xK/8PQv+SEzd7wiitPAx+mCw2By1xiNQ==",
|
"integrity": "sha512-vJzWt/znEB+JqVwtwfjkRrAYRN+ep+l070Ti8GhJnvwU4IDtVlV3T/jVNrj6rl6UChcczaJQMxVxtG5x0crlAA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/hub": "6.7.2",
|
"@sentry/hub": "6.8.0",
|
||||||
"@sentry/minimal": "6.7.2",
|
"@sentry/minimal": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2353,12 +2353,12 @@
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/hub": {
|
"node_modules/@sentry/hub": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.8.0.tgz",
|
||||||
"integrity": "sha512-05qVW6ymChJsXag4+fYCQokW3AcABIgcqrVYZUBf6GMU/Gbz5SJqpV7y1+njwWvnPZydMncP9LaDVpMKbE7UYQ==",
|
"integrity": "sha512-hFrI2Ss1fTov7CH64FJpigqRxH7YvSnGeqxT9Jc1BL7nzW/vgCK+Oh2mOZbosTcrzoDv+lE8ViOnSN3w/fo+rg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2371,12 +2371,12 @@
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/minimal": {
|
"node_modules/@sentry/minimal": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.8.0.tgz",
|
||||||
"integrity": "sha512-jkpwFv2GFHoVl5vnK+9/Q+Ea8eVdbJ3hn3/Dqq9MOLFnVK7ED6MhdHKLT79puGSFj+85OuhM5m2Q44mIhyS5mw==",
|
"integrity": "sha512-MRxUKXiiYwKjp8mOQMpTpEuIby1Jh3zRTU0cmGZtfsZ38BC1JOle8xlwC4FdtOH+VvjSYnPBMya5lgNHNPUJDQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/hub": "6.7.2",
|
"@sentry/hub": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2389,14 +2389,14 @@
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/tracing": {
|
"node_modules/@sentry/tracing": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.8.0.tgz",
|
||||||
"integrity": "sha512-juKlI7FICKONWJFJxDxerj0A+8mNRhmtrdR+OXFqOkqSAy/QXlSFZcA/j//O19k2CfwK1BrvoMcQ/4gnffUOVg==",
|
"integrity": "sha512-3gDkQnmOuOjHz5rY7BOatLEUksANU3efR8wuBa2ujsPQvoLSLFuyZpRjPPsxuUHQOqAYIbSNAoDloXECvQeHjw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/hub": "6.7.2",
|
"@sentry/hub": "6.8.0",
|
||||||
"@sentry/minimal": "6.7.2",
|
"@sentry/minimal": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2409,19 +2409,19 @@
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/types": {
|
"node_modules/@sentry/types": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.8.0.tgz",
|
||||||
"integrity": "sha512-h21Go/PfstUN+ZV6SbwRSZVg9GXRJWdLfHoO5PSVb3TVEMckuxk8tAE57/u+UZDwX8wu+Xyon2TgsKpiWKxqUg==",
|
"integrity": "sha512-PbSxqlh6Fd5thNU5f8EVYBVvX+G7XdPA+ThNb2QvSK8yv3rIf0McHTyF6sIebgJ38OYN7ZFK7vvhC/RgSAfYTA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/utils": {
|
"node_modules/@sentry/utils": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.8.0.tgz",
|
||||||
"integrity": "sha512-9COL7aaBbe61Hp5BlArtXZ1o/cxli1NGONLPrVT4fMyeQFmLonhUiy77NdsW19XnvhvaA+2IoV5dg3dnFiF/og==",
|
"integrity": "sha512-OYlI2JNrcWKMdvYbWNdQwR4QBVv2V0y5wK0U6f53nArv6RsyO5TzwRu5rMVSIZofUUqjoE5hl27jqnR+vpUrsA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2451,9 +2451,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/codemirror": {
|
"node_modules/@types/codemirror": {
|
||||||
"version": "5.60.0",
|
"version": "5.60.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.1.tgz",
|
||||||
"integrity": "sha512-xgzXZyCzedLRNC67/Nn8rpBtTFnAsX2C+Q/LGoH6zgcpF/LqdNHJMHEOhqT1bwUcSp6kQdOIuKzRbeW9DYhEhg==",
|
"integrity": "sha512-yV14LQ5VvghnW0uSuCw2bEfZC6NvxHQEckl2w3dEk5l0yPGzQh14dCaWvG5KD/2l3cgFSifR+6nIUD7LDLdUTg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/tern": "*"
|
"@types/tern": "*"
|
||||||
}
|
}
|
||||||
|
@ -2579,12 +2579,12 @@
|
||||||
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz",
|
||||||
"integrity": "sha512-KcF6p3zWhf1f8xO84tuBailV5cN92vhS+VT7UJsPzGBm9VnQqfI9AsiMUFUCYHTYPg1uCCo+HyiDnpDuvkAMfQ==",
|
"integrity": "sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/experimental-utils": "4.28.0",
|
"@typescript-eslint/experimental-utils": "4.28.1",
|
||||||
"@typescript-eslint/scope-manager": "4.28.0",
|
"@typescript-eslint/scope-manager": "4.28.1",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
"functional-red-black-tree": "^1.0.1",
|
||||||
"regexpp": "^3.1.0",
|
"regexpp": "^3.1.0",
|
||||||
|
@ -2609,14 +2609,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/experimental-utils": {
|
"node_modules/@typescript-eslint/experimental-utils": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz",
|
||||||
"integrity": "sha512-9XD9s7mt3QWMk82GoyUpc/Ji03vz4T5AYlHF9DcoFNfJ/y3UAclRsfGiE2gLfXtyC+JRA3trR7cR296TEb1oiQ==",
|
"integrity": "sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.7",
|
"@types/json-schema": "^7.0.7",
|
||||||
"@typescript-eslint/scope-manager": "4.28.0",
|
"@typescript-eslint/scope-manager": "4.28.1",
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/typescript-estree": "4.28.0",
|
"@typescript-eslint/typescript-estree": "4.28.1",
|
||||||
"eslint-scope": "^5.1.1",
|
"eslint-scope": "^5.1.1",
|
||||||
"eslint-utils": "^3.0.0"
|
"eslint-utils": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
@ -2649,13 +2649,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.1.tgz",
|
||||||
"integrity": "sha512-7x4D22oPY8fDaOCvkuXtYYTQ6mTMmkivwEzS+7iml9F9VkHGbbZ3x4fHRwxAb5KeuSkLqfnYjs46tGx2Nour4A==",
|
"integrity": "sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "4.28.0",
|
"@typescript-eslint/scope-manager": "4.28.1",
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/typescript-estree": "4.28.0",
|
"@typescript-eslint/typescript-estree": "4.28.1",
|
||||||
"debug": "^4.3.1"
|
"debug": "^4.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2675,12 +2675,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz",
|
||||||
"integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==",
|
"integrity": "sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/visitor-keys": "4.28.0"
|
"@typescript-eslint/visitor-keys": "4.28.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
|
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
|
||||||
|
@ -2691,9 +2691,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.1.tgz",
|
||||||
"integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==",
|
"integrity": "sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
|
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
|
||||||
},
|
},
|
||||||
|
@ -2703,12 +2703,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz",
|
||||||
"integrity": "sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ==",
|
"integrity": "sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/visitor-keys": "4.28.0",
|
"@typescript-eslint/visitor-keys": "4.28.1",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"globby": "^11.0.3",
|
"globby": "^11.0.3",
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
|
@ -2748,11 +2748,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz",
|
||||||
"integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==",
|
"integrity": "sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"eslint-visitor-keys": "^2.0.0"
|
"eslint-visitor-keys": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -3316,9 +3316,9 @@
|
||||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||||
},
|
},
|
||||||
"node_modules/chart.js": {
|
"node_modules/chart.js": {
|
||||||
"version": "3.3.2",
|
"version": "3.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.4.0.tgz",
|
||||||
"integrity": "sha512-H0hSO7xqTIrwxoACqnSoNromEMfXvfuVnrbuSt2TuXfBDDofbnto4zuZlRtRvC73/b37q3wGAWZyUU41QPvNbA=="
|
"integrity": "sha512-mJsRm2apQm5mwz2OgYqGNG4erZh/qljcRZkWSa0kLkFr3UC3e1wKRMgnIh6WdhUrNu0w/JT9PkjLyylqEqHXEQ=="
|
||||||
},
|
},
|
||||||
"node_modules/chartjs-adapter-moment": {
|
"node_modules/chartjs-adapter-moment": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -6770,9 +6770,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "2.52.2",
|
"version": "2.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz",
|
||||||
"integrity": "sha512-4RlFC3k2BIHlUsJ9mGd8OO+9Lm2eDF5P7+6DNQOp5sx+7N/1tFM01kELfbxlMX3MxT6owvLB1ln4S3QvvQlbUA==",
|
"integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
},
|
},
|
||||||
|
@ -9482,9 +9482,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@patternfly/patternfly": {
|
"@patternfly/patternfly": {
|
||||||
"version": "4.108.2",
|
"version": "4.115.2",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.108.2.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.115.2.tgz",
|
||||||
"integrity": "sha512-z0VB+1CXcH+eoClYQABwapX5FURSvm1nPr6asLWwg/Z4Wuxs0RjZpC6Gb+KRm8nGQwSAcMKZY1jLfPqVnznQnw=="
|
"integrity": "sha512-7hbJ4pRmj+rlXclD2F/UwceO6fS+9flGsgHc4eUc7NyTN2GXl6PLcqrjE2CtiKEPV90+KwsGQGJXZj8bz9HweA=="
|
||||||
},
|
},
|
||||||
"@polymer/font-roboto": {
|
"@polymer/font-roboto": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@ -9669,13 +9669,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/browser": {
|
"@sentry/browser": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.8.0.tgz",
|
||||||
"integrity": "sha512-Lv0Ne1QcesyGAhVcQDfQa3hDPR/MhPSDTMg3xFi+LxqztchVc4w/ynzR0wCZFb8KIHpTj5SpJHfxpDhXYMtS9g==",
|
"integrity": "sha512-nxa71csHlG5sMHUxI4e4xxuCWtbCv/QbBfMsYw7ncJSfCKG3yNlCVh8NJ7NS0rZW/MJUT6S6+r93zw0HetNDOA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/core": "6.7.2",
|
"@sentry/core": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9687,14 +9687,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/core": {
|
"@sentry/core": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.8.0.tgz",
|
||||||
"integrity": "sha512-NTZqwN5nR94yrXmSfekoPs1mIFuKvf8esdIW/DadwSKWAdLJwQTJY9xK/8PQv+SEzd7wiitPAx+mCw2By1xiNQ==",
|
"integrity": "sha512-vJzWt/znEB+JqVwtwfjkRrAYRN+ep+l070Ti8GhJnvwU4IDtVlV3T/jVNrj6rl6UChcczaJQMxVxtG5x0crlAA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "6.7.2",
|
"@sentry/hub": "6.8.0",
|
||||||
"@sentry/minimal": "6.7.2",
|
"@sentry/minimal": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9706,12 +9706,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/hub": {
|
"@sentry/hub": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.8.0.tgz",
|
||||||
"integrity": "sha512-05qVW6ymChJsXag4+fYCQokW3AcABIgcqrVYZUBf6GMU/Gbz5SJqpV7y1+njwWvnPZydMncP9LaDVpMKbE7UYQ==",
|
"integrity": "sha512-hFrI2Ss1fTov7CH64FJpigqRxH7YvSnGeqxT9Jc1BL7nzW/vgCK+Oh2mOZbosTcrzoDv+lE8ViOnSN3w/fo+rg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9723,12 +9723,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/minimal": {
|
"@sentry/minimal": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.8.0.tgz",
|
||||||
"integrity": "sha512-jkpwFv2GFHoVl5vnK+9/Q+Ea8eVdbJ3hn3/Dqq9MOLFnVK7ED6MhdHKLT79puGSFj+85OuhM5m2Q44mIhyS5mw==",
|
"integrity": "sha512-MRxUKXiiYwKjp8mOQMpTpEuIby1Jh3zRTU0cmGZtfsZ38BC1JOle8xlwC4FdtOH+VvjSYnPBMya5lgNHNPUJDQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "6.7.2",
|
"@sentry/hub": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9740,14 +9740,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/tracing": {
|
"@sentry/tracing": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.8.0.tgz",
|
||||||
"integrity": "sha512-juKlI7FICKONWJFJxDxerj0A+8mNRhmtrdR+OXFqOkqSAy/QXlSFZcA/j//O19k2CfwK1BrvoMcQ/4gnffUOVg==",
|
"integrity": "sha512-3gDkQnmOuOjHz5rY7BOatLEUksANU3efR8wuBa2ujsPQvoLSLFuyZpRjPPsxuUHQOqAYIbSNAoDloXECvQeHjw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/hub": "6.7.2",
|
"@sentry/hub": "6.8.0",
|
||||||
"@sentry/minimal": "6.7.2",
|
"@sentry/minimal": "6.8.0",
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"@sentry/utils": "6.7.2",
|
"@sentry/utils": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9759,16 +9759,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/types": {
|
"@sentry/types": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.8.0.tgz",
|
||||||
"integrity": "sha512-h21Go/PfstUN+ZV6SbwRSZVg9GXRJWdLfHoO5PSVb3TVEMckuxk8tAE57/u+UZDwX8wu+Xyon2TgsKpiWKxqUg=="
|
"integrity": "sha512-PbSxqlh6Fd5thNU5f8EVYBVvX+G7XdPA+ThNb2QvSK8yv3rIf0McHTyF6sIebgJ38OYN7ZFK7vvhC/RgSAfYTA=="
|
||||||
},
|
},
|
||||||
"@sentry/utils": {
|
"@sentry/utils": {
|
||||||
"version": "6.7.2",
|
"version": "6.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.8.0.tgz",
|
||||||
"integrity": "sha512-9COL7aaBbe61Hp5BlArtXZ1o/cxli1NGONLPrVT4fMyeQFmLonhUiy77NdsW19XnvhvaA+2IoV5dg3dnFiF/og==",
|
"integrity": "sha512-OYlI2JNrcWKMdvYbWNdQwR4QBVv2V0y5wK0U6f53nArv6RsyO5TzwRu5rMVSIZofUUqjoE5hl27jqnR+vpUrsA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/types": "6.7.2",
|
"@sentry/types": "6.8.0",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9797,9 +9797,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/codemirror": {
|
"@types/codemirror": {
|
||||||
"version": "5.60.0",
|
"version": "5.60.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.1.tgz",
|
||||||
"integrity": "sha512-xgzXZyCzedLRNC67/Nn8rpBtTFnAsX2C+Q/LGoH6zgcpF/LqdNHJMHEOhqT1bwUcSp6kQdOIuKzRbeW9DYhEhg==",
|
"integrity": "sha512-yV14LQ5VvghnW0uSuCw2bEfZC6NvxHQEckl2w3dEk5l0yPGzQh14dCaWvG5KD/2l3cgFSifR+6nIUD7LDLdUTg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/tern": "*"
|
"@types/tern": "*"
|
||||||
}
|
}
|
||||||
|
@ -9925,12 +9925,12 @@
|
||||||
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
||||||
},
|
},
|
||||||
"@typescript-eslint/eslint-plugin": {
|
"@typescript-eslint/eslint-plugin": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.1.tgz",
|
||||||
"integrity": "sha512-KcF6p3zWhf1f8xO84tuBailV5cN92vhS+VT7UJsPzGBm9VnQqfI9AsiMUFUCYHTYPg1uCCo+HyiDnpDuvkAMfQ==",
|
"integrity": "sha512-9yfcNpDaNGQ6/LQOX/KhUFTR1sCKH+PBr234k6hI9XJ0VP5UqGxap0AnNwBnWFk1MNyWBylJH9ZkzBXC+5akZQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/experimental-utils": "4.28.0",
|
"@typescript-eslint/experimental-utils": "4.28.1",
|
||||||
"@typescript-eslint/scope-manager": "4.28.0",
|
"@typescript-eslint/scope-manager": "4.28.1",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
"functional-red-black-tree": "^1.0.1",
|
||||||
"regexpp": "^3.1.0",
|
"regexpp": "^3.1.0",
|
||||||
|
@ -9939,14 +9939,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/experimental-utils": {
|
"@typescript-eslint/experimental-utils": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.1.tgz",
|
||||||
"integrity": "sha512-9XD9s7mt3QWMk82GoyUpc/Ji03vz4T5AYlHF9DcoFNfJ/y3UAclRsfGiE2gLfXtyC+JRA3trR7cR296TEb1oiQ==",
|
"integrity": "sha512-n8/ggadrZ+uyrfrSEchx3jgODdmcx7MzVM2sI3cTpI/YlfSm0+9HEUaWw3aQn2urL2KYlWYMDgn45iLfjDYB+Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/json-schema": "^7.0.7",
|
"@types/json-schema": "^7.0.7",
|
||||||
"@typescript-eslint/scope-manager": "4.28.0",
|
"@typescript-eslint/scope-manager": "4.28.1",
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/typescript-estree": "4.28.0",
|
"@typescript-eslint/typescript-estree": "4.28.1",
|
||||||
"eslint-scope": "^5.1.1",
|
"eslint-scope": "^5.1.1",
|
||||||
"eslint-utils": "^3.0.0"
|
"eslint-utils": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
@ -9962,37 +9962,37 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/parser": {
|
"@typescript-eslint/parser": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.1.tgz",
|
||||||
"integrity": "sha512-7x4D22oPY8fDaOCvkuXtYYTQ6mTMmkivwEzS+7iml9F9VkHGbbZ3x4fHRwxAb5KeuSkLqfnYjs46tGx2Nour4A==",
|
"integrity": "sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "4.28.0",
|
"@typescript-eslint/scope-manager": "4.28.1",
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/typescript-estree": "4.28.0",
|
"@typescript-eslint/typescript-estree": "4.28.1",
|
||||||
"debug": "^4.3.1"
|
"debug": "^4.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/scope-manager": {
|
"@typescript-eslint/scope-manager": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz",
|
||||||
"integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==",
|
"integrity": "sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/visitor-keys": "4.28.0"
|
"@typescript-eslint/visitor-keys": "4.28.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/types": {
|
"@typescript-eslint/types": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.1.tgz",
|
||||||
"integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA=="
|
"integrity": "sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg=="
|
||||||
},
|
},
|
||||||
"@typescript-eslint/typescript-estree": {
|
"@typescript-eslint/typescript-estree": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz",
|
||||||
"integrity": "sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ==",
|
"integrity": "sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"@typescript-eslint/visitor-keys": "4.28.0",
|
"@typescript-eslint/visitor-keys": "4.28.1",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"globby": "^11.0.3",
|
"globby": "^11.0.3",
|
||||||
"is-glob": "^4.0.1",
|
"is-glob": "^4.0.1",
|
||||||
|
@ -10016,11 +10016,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/visitor-keys": {
|
"@typescript-eslint/visitor-keys": {
|
||||||
"version": "4.28.0",
|
"version": "4.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz",
|
||||||
"integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==",
|
"integrity": "sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "4.28.0",
|
"@typescript-eslint/types": "4.28.1",
|
||||||
"eslint-visitor-keys": "^2.0.0"
|
"eslint-visitor-keys": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -10461,9 +10461,9 @@
|
||||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||||
},
|
},
|
||||||
"chart.js": {
|
"chart.js": {
|
||||||
"version": "3.3.2",
|
"version": "3.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.4.0.tgz",
|
||||||
"integrity": "sha512-H0hSO7xqTIrwxoACqnSoNromEMfXvfuVnrbuSt2TuXfBDDofbnto4zuZlRtRvC73/b37q3wGAWZyUU41QPvNbA=="
|
"integrity": "sha512-mJsRm2apQm5mwz2OgYqGNG4erZh/qljcRZkWSa0kLkFr3UC3e1wKRMgnIh6WdhUrNu0w/JT9PkjLyylqEqHXEQ=="
|
||||||
},
|
},
|
||||||
"chartjs-adapter-moment": {
|
"chartjs-adapter-moment": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -13200,9 +13200,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rollup": {
|
"rollup": {
|
||||||
"version": "2.52.2",
|
"version": "2.52.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz",
|
||||||
"integrity": "sha512-4RlFC3k2BIHlUsJ9mGd8OO+9Lm2eDF5P7+6DNQOp5sx+7N/1tFM01kELfbxlMX3MxT6owvLB1ln4S3QvvQlbUA==",
|
"integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,24 +47,24 @@
|
||||||
"@lingui/cli": "^3.10.2",
|
"@lingui/cli": "^3.10.2",
|
||||||
"@lingui/core": "^3.10.4",
|
"@lingui/core": "^3.10.4",
|
||||||
"@lingui/macro": "^3.10.2",
|
"@lingui/macro": "^3.10.2",
|
||||||
"@patternfly/patternfly": "^4.108.2",
|
"@patternfly/patternfly": "^4.115.2",
|
||||||
"@polymer/iron-form": "^3.0.1",
|
"@polymer/iron-form": "^3.0.1",
|
||||||
"@polymer/paper-input": "^3.2.1",
|
"@polymer/paper-input": "^3.2.1",
|
||||||
"@rollup/plugin-babel": "^5.3.0",
|
"@rollup/plugin-babel": "^5.3.0",
|
||||||
"@rollup/plugin-replace": "^2.4.2",
|
"@rollup/plugin-replace": "^2.4.2",
|
||||||
"@rollup/plugin-typescript": "^8.2.1",
|
"@rollup/plugin-typescript": "^8.2.1",
|
||||||
"@sentry/browser": "^6.7.2",
|
"@sentry/browser": "^6.8.0",
|
||||||
"@sentry/tracing": "^6.7.2",
|
"@sentry/tracing": "^6.8.0",
|
||||||
"@types/chart.js": "^2.9.32",
|
"@types/chart.js": "^2.9.32",
|
||||||
"@types/codemirror": "5.60.0",
|
"@types/codemirror": "5.60.1",
|
||||||
"@types/grecaptcha": "^3.0.2",
|
"@types/grecaptcha": "^3.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
"@typescript-eslint/eslint-plugin": "^4.28.1",
|
||||||
"@typescript-eslint/parser": "^4.28.0",
|
"@typescript-eslint/parser": "^4.28.1",
|
||||||
"@webcomponents/webcomponentsjs": "^2.5.0",
|
"@webcomponents/webcomponentsjs": "^2.5.0",
|
||||||
"authentik-api": "file:api",
|
"authentik-api": "file:api",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"chart.js": "^3.3.2",
|
"chart.js": "^3.4.0",
|
||||||
"chartjs-adapter-moment": "^1.0.0",
|
"chartjs-adapter-moment": "^1.0.0",
|
||||||
"codemirror": "^5.62.0",
|
"codemirror": "^5.62.0",
|
||||||
"construct-style-sheets-polyfill": "^2.4.16",
|
"construct-style-sheets-polyfill": "^2.4.16",
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
"lit-html": "^1.4.1",
|
"lit-html": "^1.4.1",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"rapidoc": "^9.0.0",
|
"rapidoc": "^9.0.0",
|
||||||
"rollup": "^2.52.2",
|
"rollup": "^2.52.3",
|
||||||
"rollup-plugin-commonjs": "^10.1.0",
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
"rollup-plugin-copy": "^3.4.0",
|
"rollup-plugin-copy": "^3.4.0",
|
||||||
"rollup-plugin-cssimport": "^1.0.2",
|
"rollup-plugin-cssimport": "^1.0.2",
|
||||||
|
|
|
@ -139,6 +139,7 @@ body {
|
||||||
/* Card */
|
/* Card */
|
||||||
.pf-c-card {
|
.pf-c-card {
|
||||||
--pf-c-card--BackgroundColor: var(--ak-dark-background-light);
|
--pf-c-card--BackgroundColor: var(--ak-dark-background-light);
|
||||||
|
color: var(--ak-dark-foreground);
|
||||||
}
|
}
|
||||||
.pf-c-card__title,
|
.pf-c-card__title,
|
||||||
.pf-c-card__body {
|
.pf-c-card__body {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { PlexAuthenticationChallenge } from "authentik-api";
|
import { PlexAuthenticationChallenge, PlexAuthenticationChallengeResponseRequest } from "authentik-api";
|
||||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||||
|
@ -15,7 +15,6 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||||
import { SourcesApi } from "authentik-api";
|
import { SourcesApi } from "authentik-api";
|
||||||
import { showMessage } from "../../../elements/messages/MessageContainer";
|
import { showMessage } from "../../../elements/messages/MessageContainer";
|
||||||
import { MessageLevel } from "../../../elements/messages/Message";
|
import { MessageLevel } from "../../../elements/messages/Message";
|
||||||
import { PlexAuthenticationChallengeResponseRequest } from "authentik-api/dist/models/PlexAuthenticationChallengeResponseRequest";
|
|
||||||
|
|
||||||
|
|
||||||
@customElement("ak-flow-sources-plex")
|
@customElement("ak-flow-sources-plex")
|
||||||
|
|
|
@ -11,9 +11,8 @@ import { BaseStage } from "../base";
|
||||||
import "../../../elements/forms/FormElement";
|
import "../../../elements/forms/FormElement";
|
||||||
import "../../../elements/EmptyState";
|
import "../../../elements/EmptyState";
|
||||||
import "../../FormStatic";
|
import "../../FormStatic";
|
||||||
import { AuthenticatorDuoChallenge, StagesApi } from "authentik-api";
|
import { AuthenticatorDuoChallenge, AuthenticatorDuoChallengeResponseRequest, StagesApi } from "authentik-api";
|
||||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||||
import { AuthenticatorDuoChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorDuoChallengeResponseRequest";
|
|
||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
@customElement("ak-stage-authenticator-duo")
|
@customElement("ak-stage-authenticator-duo")
|
||||||
|
|
|
@ -11,8 +11,7 @@ import { BaseStage } from "../base";
|
||||||
import "../../../elements/forms/FormElement";
|
import "../../../elements/forms/FormElement";
|
||||||
import "../../../elements/EmptyState";
|
import "../../../elements/EmptyState";
|
||||||
import "../../FormStatic";
|
import "../../FormStatic";
|
||||||
import { AuthenticatorStaticChallenge } from "authentik-api";
|
import { AuthenticatorStaticChallenge, AuthenticatorStaticChallengeResponseRequest } from "authentik-api";
|
||||||
import { AuthenticatorStaticChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorStaticChallengeResponseRequest";
|
|
||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
export const STATIC_TOKEN_STYLE = css`
|
export const STATIC_TOKEN_STYLE = css`
|
||||||
|
|
|
@ -12,8 +12,7 @@ import "./AuthenticatorValidateStageWebAuthn";
|
||||||
import "./AuthenticatorValidateStageCode";
|
import "./AuthenticatorValidateStageCode";
|
||||||
import "./AuthenticatorValidateStageDuo";
|
import "./AuthenticatorValidateStageDuo";
|
||||||
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
||||||
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
import { AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
||||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
|
||||||
|
|
||||||
export enum DeviceClasses {
|
export enum DeviceClasses {
|
||||||
STATIC = "static",
|
STATIC = "static",
|
||||||
|
|
|
@ -13,8 +13,7 @@ import "../../../elements/forms/FormElement";
|
||||||
import "../../../elements/EmptyState";
|
import "../../../elements/EmptyState";
|
||||||
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
||||||
import "../../FormStatic";
|
import "../../FormStatic";
|
||||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
import { AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
||||||
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
|
||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
@customElement("ak-stage-authenticator-validate-code")
|
@customElement("ak-stage-authenticator-validate-code")
|
||||||
|
|
|
@ -12,8 +12,7 @@ import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
|
||||||
import "../../../elements/forms/FormElement";
|
import "../../../elements/forms/FormElement";
|
||||||
import "../../../elements/EmptyState";
|
import "../../../elements/EmptyState";
|
||||||
import "../../FormStatic";
|
import "../../FormStatic";
|
||||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
import { AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
||||||
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
|
||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
@customElement("ak-stage-authenticator-validate-duo")
|
@customElement("ak-stage-authenticator-validate-duo")
|
||||||
|
|
|
@ -11,8 +11,7 @@ import { PFSize } from "../../../elements/Spinner";
|
||||||
import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils";
|
import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils";
|
||||||
import { BaseStage } from "../base";
|
import { BaseStage } from "../base";
|
||||||
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
|
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
|
||||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
import { AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
||||||
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
|
|
||||||
|
|
||||||
@customElement("ak-stage-authenticator-validate-webauthn")
|
@customElement("ak-stage-authenticator-validate-webauthn")
|
||||||
export class AuthenticatorValidateStageWebAuthn extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> {
|
export class AuthenticatorValidateStageWebAuthn extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> {
|
||||||
|
|
|
@ -9,8 +9,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
import AKGlobal from "../../../authentik.css";
|
import AKGlobal from "../../../authentik.css";
|
||||||
import { BaseStage } from "../base";
|
import { BaseStage } from "../base";
|
||||||
import "../../../elements/EmptyState";
|
import "../../../elements/EmptyState";
|
||||||
import { AutosubmitChallenge } from "authentik-api";
|
import { AutosubmitChallenge, AutoSubmitChallengeResponseRequest } from "authentik-api";
|
||||||
import { AutoSubmitChallengeResponseRequest } from "authentik-api/dist/models/AutoSubmitChallengeResponseRequest";
|
|
||||||
|
|
||||||
@customElement("ak-stage-autosubmit")
|
@customElement("ak-stage-autosubmit")
|
||||||
export class AutosubmitStage extends BaseStage<AutosubmitChallenge, AutoSubmitChallengeResponseRequest> {
|
export class AutosubmitStage extends BaseStage<AutosubmitChallenge, AutoSubmitChallengeResponseRequest> {
|
||||||
|
|
|
@ -698,6 +698,10 @@ msgstr "Configure how long refresh tokens and their id_tokens are valid for."
|
||||||
msgid "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected."
|
msgid "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected."
|
||||||
msgstr "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected."
|
msgstr "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected."
|
||||||
|
|
||||||
|
#: src/pages/flows/StageBindingForm.ts
|
||||||
|
msgid "Configure how the flow executor should handle an invalid response to a challenge."
|
||||||
|
msgstr "Configure how the flow executor should handle an invalid response to a challenge."
|
||||||
|
|
||||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||||
msgid "Configure how the issuer field of the ID Token should be filled."
|
msgid "Configure how the issuer field of the ID Token should be filled."
|
||||||
msgstr "Configure how the issuer field of the ID Token should be filled."
|
msgstr "Configure how the issuer field of the ID Token should be filled."
|
||||||
|
@ -941,6 +945,10 @@ msgstr "Create User"
|
||||||
msgid "Create provider"
|
msgid "Create provider"
|
||||||
msgstr "Create provider"
|
msgstr "Create provider"
|
||||||
|
|
||||||
|
#: src/pages/stages/user_write/UserWriteStageForm.ts
|
||||||
|
msgid "Create users as inactive"
|
||||||
|
msgstr "Create users as inactive"
|
||||||
|
|
||||||
#: src/pages/applications/ApplicationForm.ts
|
#: src/pages/applications/ApplicationForm.ts
|
||||||
#: src/pages/flows/BoundStagesList.ts
|
#: src/pages/flows/BoundStagesList.ts
|
||||||
#: src/pages/outposts/ServiceConnectionListPage.ts
|
#: src/pages/outposts/ServiceConnectionListPage.ts
|
||||||
|
@ -1372,8 +1380,8 @@ msgid "Evaluate policies before the Stage is present to the user."
|
||||||
msgstr "Evaluate policies before the Stage is present to the user."
|
msgstr "Evaluate policies before the Stage is present to the user."
|
||||||
|
|
||||||
#: src/pages/flows/StageBindingForm.ts
|
#: src/pages/flows/StageBindingForm.ts
|
||||||
msgid "Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with this option disabled, policies are **not** evaluated."
|
msgid "Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with both options disabled, policies are **not** evaluated."
|
||||||
msgstr "Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with this option disabled, policies are **not** evaluated."
|
msgstr "Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with both options disabled, policies are **not** evaluated."
|
||||||
|
|
||||||
#: src/pages/events/EventListPage.ts
|
#: src/pages/events/EventListPage.ts
|
||||||
msgid "Event Log"
|
msgid "Event Log"
|
||||||
|
@ -1451,9 +1459,14 @@ msgid "Explicit Consent"
|
||||||
msgstr "Explicit Consent"
|
msgstr "Explicit Consent"
|
||||||
|
|
||||||
#: src/pages/flows/FlowListPage.ts
|
#: src/pages/flows/FlowListPage.ts
|
||||||
|
#: src/pages/flows/FlowViewPage.ts
|
||||||
msgid "Export"
|
msgid "Export"
|
||||||
msgstr "Export"
|
msgstr "Export"
|
||||||
|
|
||||||
|
#: src/pages/flows/FlowViewPage.ts
|
||||||
|
msgid "Export flow"
|
||||||
|
msgstr "Export flow"
|
||||||
|
|
||||||
#: src/pages/events/EventInfo.ts
|
#: src/pages/events/EventInfo.ts
|
||||||
#: src/pages/policies/expression/ExpressionPolicyForm.ts
|
#: src/pages/policies/expression/ExpressionPolicyForm.ts
|
||||||
#: src/pages/property-mappings/PropertyMappingLDAPForm.ts
|
#: src/pages/property-mappings/PropertyMappingLDAPForm.ts
|
||||||
|
@ -1876,6 +1889,10 @@ msgstr "Internal host"
|
||||||
msgid "Internal host SSL Validation"
|
msgid "Internal host SSL Validation"
|
||||||
msgstr "Internal host SSL Validation"
|
msgstr "Internal host SSL Validation"
|
||||||
|
|
||||||
|
#: src/pages/flows/StageBindingForm.ts
|
||||||
|
msgid "Invalid response action"
|
||||||
|
msgstr "Invalid response action"
|
||||||
|
|
||||||
#: src/pages/flows/FlowForm.ts
|
#: src/pages/flows/FlowForm.ts
|
||||||
msgid "Invalidation"
|
msgid "Invalidation"
|
||||||
msgstr "Invalidation"
|
msgstr "Invalidation"
|
||||||
|
@ -2138,6 +2155,10 @@ msgstr "Logs"
|
||||||
msgid "Long-running operations which authentik executes in the background."
|
msgid "Long-running operations which authentik executes in the background."
|
||||||
msgstr "Long-running operations which authentik executes in the background."
|
msgstr "Long-running operations which authentik executes in the background."
|
||||||
|
|
||||||
|
#: src/pages/stages/user_write/UserWriteStageForm.ts
|
||||||
|
msgid "Mark newly created users as inactive."
|
||||||
|
msgstr "Mark newly created users as inactive."
|
||||||
|
|
||||||
#: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts
|
#: src/pages/policies/event_matcher/EventMatcherPolicyForm.ts
|
||||||
msgid "Match created events with this action type. When left empty, all action types will be matched."
|
msgid "Match created events with this action type. When left empty, all action types will be matched."
|
||||||
msgstr "Match created events with this action type. When left empty, all action types will be matched."
|
msgstr "Match created events with this action type. When left empty, all action types will be matched."
|
||||||
|
@ -2842,6 +2863,18 @@ msgstr "Public key, acquired from https://www.google.com/recaptcha/intro/v3.html
|
||||||
msgid "Publisher"
|
msgid "Publisher"
|
||||||
msgstr "Publisher"
|
msgstr "Publisher"
|
||||||
|
|
||||||
|
#: src/pages/flows/StageBindingForm.ts
|
||||||
|
msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
|
||||||
|
msgstr "RESTART restarts the flow from the beginning, while keeping the flow context."
|
||||||
|
|
||||||
|
#: src/pages/flows/StageBindingForm.ts
|
||||||
|
msgid "RESTART restarts the flow from the beginning."
|
||||||
|
msgstr "RESTART restarts the flow from the beginning."
|
||||||
|
|
||||||
|
#: src/pages/flows/StageBindingForm.ts
|
||||||
|
msgid "RETRY returns the error message and a similar challenge to the executor."
|
||||||
|
msgstr "RETRY returns the error message and a similar challenge to the executor."
|
||||||
|
|
||||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||||
msgid "RS256 (Asymmetric Encryption)"
|
msgid "RS256 (Asymmetric Encryption)"
|
||||||
msgstr "RS256 (Asymmetric Encryption)"
|
msgstr "RS256 (Asymmetric Encryption)"
|
||||||
|
@ -3359,6 +3392,7 @@ msgstr "Stage used to validate any authenticator. This stage should be used duri
|
||||||
#: src/pages/stages/password/PasswordStageForm.ts
|
#: src/pages/stages/password/PasswordStageForm.ts
|
||||||
#: src/pages/stages/prompt/PromptStageForm.ts
|
#: src/pages/stages/prompt/PromptStageForm.ts
|
||||||
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
#: src/pages/stages/user_login/UserLoginStageForm.ts
|
||||||
|
#: src/pages/stages/user_write/UserWriteStageForm.ts
|
||||||
msgid "Stage-specific settings"
|
msgid "Stage-specific settings"
|
||||||
msgstr "Stage-specific settings"
|
msgstr "Stage-specific settings"
|
||||||
|
|
||||||
|
@ -3816,6 +3850,16 @@ msgstr "The external URL you'll authenticate at. Can be the same domain as authe
|
||||||
msgid "The following objects use {objName}"
|
msgid "The following objects use {objName}"
|
||||||
msgstr "The following objects use {objName}"
|
msgstr "The following objects use {objName}"
|
||||||
|
|
||||||
|
#: src/pages/policies/reputation/ReputationPolicyForm.ts
|
||||||
|
msgid ""
|
||||||
|
"The policy passes when the reputation score is above the threshold, and\n"
|
||||||
|
"doesn't pass when either or both of the selected options are equal or less than the\n"
|
||||||
|
"threshold."
|
||||||
|
msgstr ""
|
||||||
|
"The policy passes when the reputation score is above the threshold, and\n"
|
||||||
|
"doesn't pass when either or both of the selected options are equal or less than the\n"
|
||||||
|
"threshold."
|
||||||
|
|
||||||
#: src/pages/policies/dummy/DummyPolicyForm.ts
|
#: src/pages/policies/dummy/DummyPolicyForm.ts
|
||||||
msgid "The policy takes a random time to execute. This controls the minimum time it will take."
|
msgid "The policy takes a random time to execute. This controls the minimum time it will take."
|
||||||
msgstr "The policy takes a random time to execute. This controls the minimum time it will take."
|
msgstr "The policy takes a random time to execute. This controls the minimum time it will take."
|
||||||
|
|
|
@ -692,6 +692,10 @@ msgstr ""
|
||||||
msgid "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected."
|
msgid "Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "Configure how the flow executor should handle an invalid response to a challenge."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
msgid "Configure how the issuer field of the ID Token should be filled."
|
msgid "Configure how the issuer field of the ID Token should be filled."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -935,6 +939,10 @@ msgstr ""
|
||||||
msgid "Create provider"
|
msgid "Create provider"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "Create users as inactive"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
#:
|
#:
|
||||||
#:
|
#:
|
||||||
|
@ -1364,7 +1372,7 @@ msgid "Evaluate policies before the Stage is present to the user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
msgid "Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with this option disabled, policies are **not** evaluated."
|
msgid "Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with both options disabled, policies are **not** evaluated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
|
@ -1442,10 +1450,15 @@ msgstr ""
|
||||||
msgid "Explicit Consent"
|
msgid "Explicit Consent"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
#:
|
#:
|
||||||
msgid "Export"
|
msgid "Export"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "Export flow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
#:
|
#:
|
||||||
#:
|
#:
|
||||||
|
@ -1868,6 +1881,10 @@ msgstr ""
|
||||||
msgid "Internal host SSL Validation"
|
msgid "Internal host SSL Validation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "Invalid response action"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
msgid "Invalidation"
|
msgid "Invalidation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -2130,6 +2147,10 @@ msgstr ""
|
||||||
msgid "Long-running operations which authentik executes in the background."
|
msgid "Long-running operations which authentik executes in the background."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "Mark newly created users as inactive."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
msgid "Match created events with this action type. When left empty, all action types will be matched."
|
msgid "Match created events with this action type. When left empty, all action types will be matched."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -2834,6 +2855,18 @@ msgstr ""
|
||||||
msgid "Publisher"
|
msgid "Publisher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "RESTART restarts the flow from the beginning."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "RETRY returns the error message and a similar challenge to the executor."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
msgid "RS256 (Asymmetric Encryption)"
|
msgid "RS256 (Asymmetric Encryption)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -3351,6 +3384,7 @@ msgstr ""
|
||||||
#:
|
#:
|
||||||
#:
|
#:
|
||||||
#:
|
#:
|
||||||
|
#:
|
||||||
msgid "Stage-specific settings"
|
msgid "Stage-specific settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3808,6 +3842,13 @@ msgstr ""
|
||||||
msgid "The following objects use {objName}"
|
msgid "The following objects use {objName}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid ""
|
||||||
|
"The policy passes when the reputation score is above the threshold, and\n"
|
||||||
|
"doesn't pass when either or both of the selected options are equal or less than the\n"
|
||||||
|
"threshold."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
msgid "The policy takes a random time to execute. This controls the minimum time it will take."
|
msgid "The policy takes a random time to execute. This controls the minimum time it will take."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
import { EventMatcherPolicyActionEnum, FlowsApi } from "authentik-api";
|
import { EventActions, FlowsApi } from "authentik-api";
|
||||||
import "../../elements/Spinner";
|
import "../../elements/Spinner";
|
||||||
import "../../elements/Expand";
|
import "../../elements/Expand";
|
||||||
import { PFSize } from "../../elements/Spinner";
|
import { PFSize } from "../../elements/Spinner";
|
||||||
|
@ -189,14 +189,14 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`;
|
return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`;
|
||||||
}
|
}
|
||||||
switch (this.event?.action) {
|
switch (this.event?.action) {
|
||||||
case EventMatcherPolicyActionEnum.ModelCreated:
|
case EventActions.ModelCreated:
|
||||||
case EventMatcherPolicyActionEnum.ModelUpdated:
|
case EventActions.ModelUpdated:
|
||||||
case EventMatcherPolicyActionEnum.ModelDeleted:
|
case EventActions.ModelDeleted:
|
||||||
return html`
|
return html`
|
||||||
<h3>${t`Affected model:`}</h3>
|
<h3>${t`Affected model:`}</h3>
|
||||||
${this.getModelInfo(this.event.context?.model as EventModel)}
|
${this.getModelInfo(this.event.context?.model as EventModel)}
|
||||||
`;
|
`;
|
||||||
case EventMatcherPolicyActionEnum.AuthorizeApplication:
|
case EventActions.AuthorizeApplication:
|
||||||
return html`<div class="pf-l-flex">
|
return html`<div class="pf-l-flex">
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
<h3>${t`Authorized application:`}</h3>
|
<h3>${t`Authorized application:`}</h3>
|
||||||
|
@ -213,17 +213,17 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.EmailSent:
|
case EventActions.EmailSent:
|
||||||
return html`<h3>${t`Email info:`}</h3>
|
return html`<h3>${t`Email info:`}</h3>
|
||||||
${this.getEmailInfo(this.event.context)}
|
${this.getEmailInfo(this.event.context)}
|
||||||
<ak-expand>
|
<ak-expand>
|
||||||
<iframe srcdoc=${this.event.context.body}></iframe>
|
<iframe srcdoc=${this.event.context.body}></iframe>
|
||||||
</ak-expand>`;
|
</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.SecretView:
|
case EventActions.SecretView:
|
||||||
return html`
|
return html`
|
||||||
<h3>${t`Secret:`}</h3>
|
<h3>${t`Secret:`}</h3>
|
||||||
${this.getModelInfo(this.event.context.secret as EventModel)}`;
|
${this.getModelInfo(this.event.context.secret as EventModel)}`;
|
||||||
case EventMatcherPolicyActionEnum.SystemException:
|
case EventActions.SystemException:
|
||||||
return html`
|
return html`
|
||||||
<a
|
<a
|
||||||
class="pf-c-button pf-m-primary"
|
class="pf-c-button pf-m-primary"
|
||||||
|
@ -240,7 +240,7 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.PropertyMappingException:
|
case EventActions.PropertyMappingException:
|
||||||
return html`<div class="pf-l-flex">
|
return html`<div class="pf-l-flex">
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
<h3>${t`Exception`}</h3>
|
<h3>${t`Exception`}</h3>
|
||||||
|
@ -252,7 +252,7 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.PolicyException:
|
case EventActions.PolicyException:
|
||||||
return html`<div class="pf-l-flex">
|
return html`<div class="pf-l-flex">
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
<h3>${t`Binding`}</h3>
|
<h3>${t`Binding`}</h3>
|
||||||
|
@ -271,7 +271,7 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.PolicyExecution:
|
case EventActions.PolicyExecution:
|
||||||
return html`<div class="pf-l-flex">
|
return html`<div class="pf-l-flex">
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
<h3>${t`Binding`}</h3>
|
<h3>${t`Binding`}</h3>
|
||||||
|
@ -299,10 +299,10 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.ConfigurationError:
|
case EventActions.ConfigurationError:
|
||||||
return html`<h3>${this.event.context.message}</h3>
|
return html`<h3>${this.event.context.message}</h3>
|
||||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.UpdateAvailable:
|
case EventActions.UpdateAvailable:
|
||||||
return html`<h3>${t`New version available!`}</h3>
|
return html`<h3>${t`New version available!`}</h3>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@ -311,7 +311,7 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
</a>`;
|
</a>`;
|
||||||
// Action types which typically don't record any extra context.
|
// Action types which typically don't record any extra context.
|
||||||
// If context is not empty, we fall to the default response.
|
// If context is not empty, we fall to the default response.
|
||||||
case EventMatcherPolicyActionEnum.Login:
|
case EventActions.Login:
|
||||||
if ("using_source" in this.event.context) {
|
if ("using_source" in this.event.context) {
|
||||||
return html`<div class="pf-l-flex">
|
return html`<div class="pf-l-flex">
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
|
@ -321,11 +321,11 @@ new?labels=bug,from_authentik&title=${encodeURIComponent(title)}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
return this.defaultResponse();
|
return this.defaultResponse();
|
||||||
case EventMatcherPolicyActionEnum.LoginFailed:
|
case EventActions.LoginFailed:
|
||||||
return html`
|
return html`
|
||||||
<h3>${t`Attempted to log in as ${this.event.context.username}`}</h3>
|
<h3>${t`Attempted to log in as ${this.event.context.username}`}</h3>
|
||||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.Logout:
|
case EventActions.Logout:
|
||||||
if (this.event.context === {}) {
|
if (this.event.context === {}) {
|
||||||
return html`<span>${t`No additional data available.`}</span>`;
|
return html`<span>${t`No additional data available.`}</span>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||||
import { EventsApi } from "authentik-api";
|
import { EventsApi } from "authentik-api";
|
||||||
import { DEFAULT_CONFIG } from "../../api/Config";
|
import { DEFAULT_CONFIG } from "../../api/Config";
|
||||||
import { EventWithContext } from "../../api/Events";
|
import { EventWithContext } from "../../api/Events";
|
||||||
|
@ -27,11 +27,7 @@ export class EventInfoPage extends LitElement {
|
||||||
event!: EventWithContext;
|
event!: EventWithContext;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFPage, PFContent, PFCard, AKGlobal].concat(css`
|
return [PFBase, PFPage, PFContent, PFCard, AKGlobal];
|
||||||
.pf-c-card {
|
|
||||||
color: var(--ak-dark-foreground);
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
|
|
|
@ -51,9 +51,9 @@ export class FlowViewPage extends LitElement {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`<ak-page-header
|
return html`<ak-page-header
|
||||||
icon="pf-icon pf-icon-process-automation"
|
icon="pf-icon pf-icon-process-automation"
|
||||||
header=${this.flow.name}
|
header=${this.flow.name}
|
||||||
description=${this.flow.title}>
|
description=${this.flow.title}>
|
||||||
</ak-page-header>
|
</ak-page-header>
|
||||||
<ak-tabs>
|
<ak-tabs>
|
||||||
<div slot="page-overview" data-tab-title="${t`Flow Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
<div slot="page-overview" data-tab-title="${t`Flow Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
|
@ -69,7 +69,7 @@ export class FlowViewPage extends LitElement {
|
||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">
|
<div class="pf-c-description-list__text">
|
||||||
<button
|
<button
|
||||||
class="pf-c-button pf-m-secondary"
|
class="pf-c-button pf-m-primary"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
new FlowsApi(DEFAULT_CONFIG).flowsInstancesExecuteRetrieve({
|
new FlowsApi(DEFAULT_CONFIG).flowsInstancesExecuteRetrieve({
|
||||||
slug: this.flow.slug
|
slug: this.flow.slug
|
||||||
|
@ -82,6 +82,16 @@ export class FlowViewPage extends LitElement {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt class="pf-c-description-list__term">
|
||||||
|
<span class="pf-c-description-list__text">${t`Export flow`}</span>
|
||||||
|
</dt>
|
||||||
|
<dd class="pf-c-description-list__description">
|
||||||
|
<div class="pf-c-description-list__text">
|
||||||
|
<a class="pf-c-button pf-m-secondary" href=${this.flow.exportUrl}>
|
||||||
|
${t`Export`}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FlowsApi, FlowStageBinding, PolicyEngineMode, Stage, StagesApi } from "authentik-api";
|
import { FlowsApi, FlowStageBinding, InvalidResponseActionEnum, PolicyEngineMode, Stage, StagesApi } from "authentik-api";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { customElement, property } from "lit-element";
|
import { customElement, property } from "lit-element";
|
||||||
import { html, TemplateResult } from "lit-html";
|
import { html, TemplateResult } from "lit-html";
|
||||||
|
@ -123,7 +123,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${t`Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with this option disabled, policies are **not** evaluated.`}
|
${t`Evaluate policies during the Flow planning process. Disable this for input-based policies. Should be used in conjunction with 'Re-evaluate policies', as with both options disabled, policies are **not** evaluated.`}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal name="reEvaluatePolicies">
|
<ak-form-element-horizontal name="reEvaluatePolicies">
|
||||||
|
@ -135,6 +135,23 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||||
</div>
|
</div>
|
||||||
<p class="pf-c-form__helper-text">${t`Evaluate policies before the Stage is present to the user.`}</p>
|
<p class="pf-c-form__helper-text">${t`Evaluate policies before the Stage is present to the user.`}</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Invalid response action`}
|
||||||
|
?required=${true}
|
||||||
|
name="invalidResponseAction">
|
||||||
|
<select class="pf-c-form-control">
|
||||||
|
<option value=${InvalidResponseActionEnum.Retry} ?selected=${this.instance?.invalidResponseAction === InvalidResponseActionEnum.Retry}>
|
||||||
|
${t`RETRY returns the error message and a similar challenge to the executor.`}
|
||||||
|
</option>
|
||||||
|
<option value=${InvalidResponseActionEnum.Restart} ?selected=${this.instance?.invalidResponseAction === InvalidResponseActionEnum.Restart}>
|
||||||
|
${t`RESTART restarts the flow from the beginning.`}
|
||||||
|
</option>
|
||||||
|
<option value=${InvalidResponseActionEnum.RestartWithContext} ?selected=${this.instance?.invalidResponseAction === InvalidResponseActionEnum.RestartWithContext}>
|
||||||
|
${t`RESTART restarts the flow from the beginning, while keeping the flow context.`}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<p class="pf-c-form__helper-text">${t`Configure how the flow executor should handle an invalid response to a challenge.`}</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${t`Policy engine mode`}
|
label=${t`Policy engine mode`}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
|
|
|
@ -44,6 +44,11 @@ export class ReputationPolicyForm extends ModelForm<ReputationPolicy, string> {
|
||||||
<div class="form-help-text">
|
<div class="form-help-text">
|
||||||
${t`Allows/denys requests based on the users and/or the IPs reputation.`}
|
${t`Allows/denys requests based on the users and/or the IPs reputation.`}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-help-text">
|
||||||
|
${t`The policy passes when the reputation score is above the threshold, and
|
||||||
|
doesn't pass when either or both of the selected options are equal or less than the
|
||||||
|
threshold.`}
|
||||||
|
</div>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${t`Name`}
|
label=${t`Name`}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
|
|
|
@ -102,7 +102,7 @@ export class LDAPProviderViewPage extends LitElement {
|
||||||
</span>
|
</span>
|
||||||
<ak-provider-ldap-form
|
<ak-provider-ldap-form
|
||||||
slot="form"
|
slot="form"
|
||||||
.instancePk=${this.provider.pk || 0}>
|
.instancePk=${this.provider.pk}>
|
||||||
</ak-provider-ldap-form>
|
</ak-provider-ldap-form>
|
||||||
<button slot="trigger" class="pf-c-button pf-m-primary">
|
<button slot="trigger" class="pf-c-button pf-m-primary">
|
||||||
${t`Edit`}
|
${t`Edit`}
|
||||||
|
|
|
@ -5,7 +5,9 @@ import { html, TemplateResult } from "lit-html";
|
||||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
import "../../../elements/forms/HorizontalFormElement";
|
import "../../../elements/forms/HorizontalFormElement";
|
||||||
|
import "../../../elements/forms/FormGroup";
|
||||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||||
|
import { first } from "../../../utils";
|
||||||
|
|
||||||
@customElement("ak-stage-user-write-form")
|
@customElement("ak-stage-user-write-form")
|
||||||
export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
|
export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
|
||||||
|
@ -49,6 +51,22 @@ export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
|
||||||
name="name">
|
name="name">
|
||||||
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
<input type="text" value="${ifDefined(this.instance?.name || "")}" class="pf-c-form-control" required>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-group .expanded=${true}>
|
||||||
|
<span slot="header">
|
||||||
|
${t`Stage-specific settings`}
|
||||||
|
</span>
|
||||||
|
<div slot="body" class="pf-c-form">
|
||||||
|
<ak-form-element-horizontal name="createUsersAsInactive">
|
||||||
|
<div class="pf-c-check">
|
||||||
|
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.createUsersAsInactive, true)}>
|
||||||
|
<label class="pf-c-check__label">
|
||||||
|
${t`Create users as inactive`}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="pf-c-form__helper-text">${t`Mark newly created users as inactive.`}</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
|
</div>
|
||||||
|
</ak-form-group>
|
||||||
</form>`;
|
</form>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,7 @@ export class TenantForm extends ModelForm<Tenant, string> {
|
||||||
<option value="" ?selected=${this.instance?.flowUnenrollment === undefined}>---------</option>
|
<option value="" ?selected=${this.instance?.flowUnenrollment === undefined}>---------</option>
|
||||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||||
ordering: "pk",
|
ordering: "pk",
|
||||||
designation: FlowsInstancesListDesignationEnum.Recovery,
|
designation: FlowsInstancesListDesignationEnum.Unenrollment,
|
||||||
}).then(flows => {
|
}).then(flows => {
|
||||||
return flows.results.map(flow => {
|
return flows.results.map(flow => {
|
||||||
const selected = this.instance?.flowUnenrollment === flow.pk;
|
const selected = this.instance?.flowUnenrollment === flow.pk;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
|
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||||
|
|
||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
|
@ -27,6 +27,7 @@ import "./settings/UserSettingsAuthenticatorTOTP";
|
||||||
import "./settings/UserSettingsAuthenticatorWebAuthn";
|
import "./settings/UserSettingsAuthenticatorWebAuthn";
|
||||||
import "./settings/UserSettingsPassword";
|
import "./settings/UserSettingsPassword";
|
||||||
import "./settings/SourceSettingsOAuth";
|
import "./settings/SourceSettingsOAuth";
|
||||||
|
import { EVENT_REFRESH } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-user-settings")
|
@customElement("ak-user-settings")
|
||||||
export class UserSettingsPage extends LitElement {
|
export class UserSettingsPage extends LitElement {
|
||||||
|
@ -35,6 +36,24 @@ export class UserSettingsPage extends LitElement {
|
||||||
return [PFBase, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, PFForm, PFFormControl, AKGlobal];
|
return [PFBase, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, PFForm, PFFormControl, AKGlobal];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property({attribute: false})
|
||||||
|
userSettings?: Promise<UserSetting[]>;
|
||||||
|
|
||||||
|
@property({attribute: false})
|
||||||
|
sourceSettings?: Promise<UserSetting[]>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.addEventListener(EVENT_REFRESH, () => {
|
||||||
|
this.firstUpdated();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated(): void {
|
||||||
|
this.userSettings = new StagesApi(DEFAULT_CONFIG).stagesAllUserSettingsList();
|
||||||
|
this.sourceSettings = new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettingsList();
|
||||||
|
}
|
||||||
|
|
||||||
renderStageSettings(stage: UserSetting): TemplateResult {
|
renderStageSettings(stage: UserSetting): TemplateResult {
|
||||||
switch (stage.component) {
|
switch (stage.component) {
|
||||||
case "ak-user-settings-authenticator-webauthn":
|
case "ak-user-settings-authenticator-webauthn":
|
||||||
|
@ -82,14 +101,14 @@ export class UserSettingsPage extends LitElement {
|
||||||
<section slot="page-tokens" data-tab-title="${t`Tokens`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
<section slot="page-tokens" data-tab-title="${t`Tokens`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
<ak-user-token-list></ak-user-token-list>
|
<ak-user-token-list></ak-user-token-list>
|
||||||
</section>
|
</section>
|
||||||
${until(new StagesApi(DEFAULT_CONFIG).stagesAllUserSettingsList().then((stages) => {
|
${until(this.userSettings?.then((stages) => {
|
||||||
return stages.map((stage) => {
|
return stages.map((stage) => {
|
||||||
return html`<section slot="page-${stage.objectUid}" data-tab-title="${ifDefined(stage.title)}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
return html`<section slot="page-${stage.objectUid}" data-tab-title="${ifDefined(stage.title)}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
${this.renderStageSettings(stage)}
|
${this.renderStageSettings(stage)}
|
||||||
</section>`;
|
</section>`;
|
||||||
});
|
});
|
||||||
}))}
|
}))}
|
||||||
${until(new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettingsList().then((source) => {
|
${until(this.sourceSettings?.then((source) => {
|
||||||
return source.map((stage) => {
|
return source.map((stage) => {
|
||||||
return html`<section slot="page-${stage.objectUid}" data-tab-title="${ifDefined(stage.title)}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
return html`<section slot="page-${stage.objectUid}" data-tab-title="${ifDefined(stage.title)}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
${this.renderSourceSettings(stage)}
|
${this.renderSourceSettings(stage)}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { customElement, html, TemplateResult } from "lit-element";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||||
import { BaseUserSettings } from "./BaseUserSettings";
|
import { BaseUserSettings } from "./BaseUserSettings";
|
||||||
|
import { EVENT_REFRESH } from "../../../constants";
|
||||||
|
|
||||||
@customElement("ak-user-settings-authenticator-duo")
|
@customElement("ak-user-settings-authenticator-duo")
|
||||||
export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
|
export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
|
||||||
|
@ -27,7 +28,12 @@ export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
|
||||||
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsDuoDestroy({
|
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsDuoDestroy({
|
||||||
id: devices.results[0].pk || 0
|
id: devices.results[0].pk || 0
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.requestUpdate();
|
this.dispatchEvent(
|
||||||
|
new CustomEvent(EVENT_REFRESH, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { until } from "lit-html/directives/until";
|
||||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||||
import { STATIC_TOKEN_STYLE } from "../../../flows/stages/authenticator_static/AuthenticatorStaticStage";
|
import { STATIC_TOKEN_STYLE } from "../../../flows/stages/authenticator_static/AuthenticatorStaticStage";
|
||||||
import { BaseUserSettings } from "./BaseUserSettings";
|
import { BaseUserSettings } from "./BaseUserSettings";
|
||||||
|
import { EVENT_REFRESH } from "../../../constants";
|
||||||
|
|
||||||
@customElement("ak-user-settings-authenticator-static")
|
@customElement("ak-user-settings-authenticator-static")
|
||||||
export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
|
export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
|
||||||
|
@ -42,7 +43,12 @@ export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
|
||||||
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsStaticDestroy({
|
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsStaticDestroy({
|
||||||
id: devices.results[0].pk || 0
|
id: devices.results[0].pk || 0
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.requestUpdate();
|
this.dispatchEvent(
|
||||||
|
new CustomEvent(EVENT_REFRESH, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { customElement, html, TemplateResult } from "lit-element";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||||
import { BaseUserSettings } from "./BaseUserSettings";
|
import { BaseUserSettings } from "./BaseUserSettings";
|
||||||
|
import { EVENT_REFRESH } from "../../../constants";
|
||||||
|
|
||||||
@customElement("ak-user-settings-authenticator-totp")
|
@customElement("ak-user-settings-authenticator-totp")
|
||||||
export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
|
export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
|
||||||
|
@ -27,7 +28,12 @@ export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
|
||||||
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsTotpDestroy({
|
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsTotpDestroy({
|
||||||
id: devices.results[0].pk || 0
|
id: devices.results[0].pk || 0
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.requestUpdate();
|
this.dispatchEvent(
|
||||||
|
new CustomEvent(EVENT_REFRESH, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -12,6 +12,7 @@ import "../../../elements/forms/Form";
|
||||||
import "../../../elements/forms/ModalForm";
|
import "../../../elements/forms/ModalForm";
|
||||||
import "../../../elements/forms/HorizontalFormElement";
|
import "../../../elements/forms/HorizontalFormElement";
|
||||||
import { ifDefined } from "lit-html/directives/if-defined";
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
import { EVENT_REFRESH } from "../../../constants";
|
||||||
|
|
||||||
@customElement("ak-user-settings-authenticator-webauthn")
|
@customElement("ak-user-settings-authenticator-webauthn")
|
||||||
export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
|
export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
|
||||||
|
@ -28,7 +29,12 @@ export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
|
||||||
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnDestroy({
|
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnDestroy({
|
||||||
id: device.pk || 0
|
id: device.pk || 0
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.requestUpdate();
|
this.dispatchEvent(
|
||||||
|
new CustomEvent(EVENT_REFRESH, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
<button slot="trigger" class="pf-c-button pf-m-danger">
|
<button slot="trigger" class="pf-c-button pf-m-danger">
|
||||||
|
|
|
@ -57,7 +57,7 @@ import TabItem from '@theme/TabItem';
|
||||||
location @akprox_signin {
|
location @akprox_signin {
|
||||||
internal;
|
internal;
|
||||||
add_header Set-Cookie $auth_cookie;
|
add_header Set-Cookie $auth_cookie;
|
||||||
return 302 /akprox/start?rd=$escaped_request_uri;
|
return 302 /akprox/start?rd=$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
|
|
@ -116,6 +116,29 @@ slug: "2021.6"
|
||||||
- web/admin: handle elements in slot=form not being forms
|
- web/admin: handle elements in slot=form not being forms
|
||||||
- web/admin: sort inputs on authenticator validation stage form
|
- web/admin: sort inputs on authenticator validation stage form
|
||||||
|
|
||||||
|
## Fixed in 2021.6.3
|
||||||
|
|
||||||
|
- api: use partition instead of split for token
|
||||||
|
- core: fix flow background not correctly loading on initial draw
|
||||||
|
- events: add ability to create events via API
|
||||||
|
- events: ignore notification non-existent in transport
|
||||||
|
- events: only create SYSTEM_EXCEPTION event when error would've been sent to sentry
|
||||||
|
- expressions: fix regex_match result being inverted
|
||||||
|
- flows: add FlowStageBinding to flow plan instead of just stage
|
||||||
|
- flows: add invalid_response_action to configure how the FlowExecutor should handle invalid responses
|
||||||
|
- flows: handle possible errors with FlowPlans received from cache
|
||||||
|
- outposts: check docker container ports match
|
||||||
|
- outposts/ldap: fixed IsActive and IsSuperuser returning swapped incorrect values (#1078)
|
||||||
|
- providers/oauth2: fix exp of JWT when not using seconds
|
||||||
|
- sources/ldap: improve error handling when checking for password complexity on non-ad setups
|
||||||
|
- stages/authenticator_duo: fix component not being set in API
|
||||||
|
- stages/prompt: ensure hidden and static fields keep the value they had set
|
||||||
|
- stages/user_write: add flag to create new users as inactive
|
||||||
|
- tenants: include all default flows in current_tenant
|
||||||
|
- web/admin: fix deletion of authenticator not reloading the state correctly
|
||||||
|
- web/admin: fix only recovery flows being selectable for unenrollment flow in tenant form
|
||||||
|
- web/admin: fix text color on pf-c-card
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
|
|
||||||
This release does not introduce any new requirements.
|
This release does not introduce any new requirements.
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
"react-toggle": "^4.1.2"
|
"react-toggle": "^4.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "2.3.1"
|
"prettier": "2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@algolia/autocomplete-core": {
|
"node_modules/@algolia/autocomplete-core": {
|
||||||
|
@ -10376,9 +10376,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
|
||||||
"integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==",
|
"integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin-prettier.js"
|
||||||
|
@ -22886,9 +22886,9 @@
|
||||||
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
|
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
|
||||||
"integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==",
|
"integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pretty-error": {
|
"pretty-error": {
|
||||||
|
|
|
@ -35,6 +35,6 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "2.3.1"
|
"prettier": "2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,9 @@
|
||||||
"name": "default-enrollment-user-write"
|
"name": "default-enrollment-user-write"
|
||||||
},
|
},
|
||||||
"model": "authentik_stages_user_write.userwritestage",
|
"model": "authentik_stages_user_write.userwritestage",
|
||||||
"attrs": {}
|
"attrs": {
|
||||||
|
"create_users_as_inactive": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"identifiers": {
|
"identifiers": {
|
||||||
|
|
Reference in New Issue