2021-02-20 17:58:50 +00:00
|
|
|
"""Challenge helpers"""
|
2021-02-17 22:52:49 +00:00
|
|
|
from enum import Enum
|
2021-02-20 19:16:20 +00:00
|
|
|
from typing import TYPE_CHECKING, Optional
|
2021-02-17 22:52:49 +00:00
|
|
|
|
|
|
|
from django.http import JsonResponse
|
2021-02-20 22:19:27 +00:00
|
|
|
from rest_framework.fields import ChoiceField, DictField
|
2021-03-30 13:50:00 +00:00
|
|
|
from rest_framework.serializers import CharField
|
2021-02-17 22:52:49 +00:00
|
|
|
|
2021-03-30 13:50:00 +00:00
|
|
|
from authentik.core.api.utils import PassiveSerializer
|
2021-02-20 17:58:50 +00:00
|
|
|
from authentik.flows.transfer.common import DataclassEncoder
|
|
|
|
|
2021-02-20 19:16:20 +00:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from authentik.flows.stage import StageView
|
|
|
|
|
2022-05-14 10:06:19 +00:00
|
|
|
PLAN_CONTEXT_TITLE = "title"
|
|
|
|
PLAN_CONTEXT_URL = "url"
|
|
|
|
PLAN_CONTEXT_ATTRS = "attrs"
|
|
|
|
|
2021-02-17 22:52:49 +00:00
|
|
|
|
|
|
|
class ChallengeTypes(Enum):
|
2021-02-20 17:58:50 +00:00
|
|
|
"""Currently defined challenge types"""
|
2021-02-17 22:52:49 +00:00
|
|
|
|
2021-03-30 08:05:14 +00:00
|
|
|
NATIVE = "native"
|
|
|
|
SHELL = "shell"
|
|
|
|
REDIRECT = "redirect"
|
2021-02-20 22:19:27 +00:00
|
|
|
|
|
|
|
|
2021-03-30 13:50:00 +00:00
|
|
|
class ErrorDetailSerializer(PassiveSerializer):
|
2021-02-20 22:19:27 +00:00
|
|
|
"""Serializer for rest_framework's error messages"""
|
|
|
|
|
|
|
|
string = CharField()
|
|
|
|
code = CharField()
|
|
|
|
|
|
|
|
|
2021-06-08 14:53:28 +00:00
|
|
|
class ContextualFlowInfo(PassiveSerializer):
|
|
|
|
"""Contextual flow information for a challenge"""
|
|
|
|
|
|
|
|
title = CharField(required=False, allow_blank=True)
|
|
|
|
background = CharField(required=False)
|
|
|
|
cancel_url = CharField()
|
|
|
|
|
|
|
|
|
2021-03-30 13:50:00 +00:00
|
|
|
class Challenge(PassiveSerializer):
|
2021-02-20 17:58:50 +00:00
|
|
|
"""Challenge that gets sent to the client based on which stage
|
|
|
|
is currently active"""
|
2021-02-17 22:52:49 +00:00
|
|
|
|
2021-03-08 10:14:00 +00:00
|
|
|
type = ChoiceField(
|
2021-04-04 14:15:50 +00:00
|
|
|
choices=[(x.value, x.name) for x in ChallengeTypes],
|
2021-03-08 10:14:00 +00:00
|
|
|
)
|
2021-06-08 15:56:35 +00:00
|
|
|
flow_info = ContextualFlowInfo(required=False)
|
2021-05-24 12:08:54 +00:00
|
|
|
component = CharField(default="")
|
2021-02-20 18:41:32 +00:00
|
|
|
|
2021-02-20 22:19:27 +00:00
|
|
|
response_errors = DictField(
|
2021-03-24 10:57:56 +00:00
|
|
|
child=ErrorDetailSerializer(many=True), allow_empty=True, required=False
|
2021-02-20 22:19:27 +00:00
|
|
|
)
|
|
|
|
|
2021-02-17 22:52:49 +00:00
|
|
|
|
2021-02-20 22:19:27 +00:00
|
|
|
class RedirectChallenge(Challenge):
|
|
|
|
"""Challenge type to redirect the client"""
|
|
|
|
|
|
|
|
to = CharField()
|
2021-05-24 12:08:54 +00:00
|
|
|
component = CharField(default="xak-flow-redirect")
|
2021-02-20 22:19:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ShellChallenge(Challenge):
|
2021-05-24 12:08:54 +00:00
|
|
|
"""challenge type to render HTML as-is"""
|
2021-02-20 22:19:27 +00:00
|
|
|
|
|
|
|
body = CharField()
|
2021-05-24 12:08:54 +00:00
|
|
|
component = CharField(default="xak-flow-shell")
|
2021-02-20 22:19:27 +00:00
|
|
|
|
|
|
|
|
2021-02-21 12:15:45 +00:00
|
|
|
class WithUserInfoChallenge(Challenge):
|
|
|
|
"""Challenge base which shows some user info"""
|
|
|
|
|
2021-05-25 10:53:48 +00:00
|
|
|
pending_user = CharField(allow_blank=True)
|
2021-02-21 12:15:45 +00:00
|
|
|
pending_user_avatar = CharField()
|
|
|
|
|
|
|
|
|
2022-01-01 18:45:34 +00:00
|
|
|
class AccessDeniedChallenge(WithUserInfoChallenge):
|
2021-03-23 16:23:44 +00:00
|
|
|
"""Challenge when a flow's active stage calls `stage_invalid()`."""
|
|
|
|
|
|
|
|
error_message = CharField(required=False)
|
2021-05-24 12:08:54 +00:00
|
|
|
component = CharField(default="ak-stage-access-denied")
|
2021-03-23 16:23:44 +00:00
|
|
|
|
|
|
|
|
2021-03-30 13:50:00 +00:00
|
|
|
class PermissionSerializer(PassiveSerializer):
|
2021-02-21 12:15:45 +00:00
|
|
|
"""Permission used for consent"""
|
|
|
|
|
|
|
|
name = CharField()
|
|
|
|
id = CharField()
|
|
|
|
|
|
|
|
|
2021-03-30 13:50:00 +00:00
|
|
|
class ChallengeResponse(PassiveSerializer):
|
2021-02-20 17:58:50 +00:00
|
|
|
"""Base class for all challenge responses"""
|
2021-02-17 22:52:49 +00:00
|
|
|
|
2021-02-20 19:16:20 +00:00
|
|
|
stage: Optional["StageView"]
|
2021-05-24 18:04:56 +00:00
|
|
|
component = CharField(default="xak-flow-response-default")
|
2021-02-20 19:16:20 +00:00
|
|
|
|
2021-03-08 10:14:00 +00:00
|
|
|
def __init__(self, instance=None, data=None, **kwargs):
|
2021-02-20 19:16:20 +00:00
|
|
|
self.stage = kwargs.pop("stage", None)
|
|
|
|
super().__init__(instance=instance, data=data, **kwargs)
|
|
|
|
|
2021-02-17 22:52:49 +00:00
|
|
|
|
2022-05-14 10:06:19 +00:00
|
|
|
class AutosubmitChallenge(Challenge):
|
|
|
|
"""Autosubmit challenge used to send and navigate a POST request"""
|
|
|
|
|
|
|
|
url = CharField()
|
|
|
|
attrs = DictField(child=CharField())
|
|
|
|
title = CharField(required=False)
|
|
|
|
component = CharField(default="ak-stage-autosubmit")
|
|
|
|
|
|
|
|
|
|
|
|
class AutoSubmitChallengeResponse(ChallengeResponse):
|
|
|
|
"""Pseudo class for autosubmit response"""
|
|
|
|
|
|
|
|
component = CharField(default="ak-stage-autosubmit")
|
|
|
|
|
|
|
|
|
2021-02-17 22:52:49 +00:00
|
|
|
class HttpChallengeResponse(JsonResponse):
|
2021-02-20 17:58:50 +00:00
|
|
|
"""Subclass of JsonResponse that uses the `DataclassEncoder`"""
|
|
|
|
|
2021-02-20 22:19:27 +00:00
|
|
|
def __init__(self, challenge, **kwargs) -> None:
|
2021-02-20 17:58:50 +00:00
|
|
|
# pyright: reportGeneralTypeIssues=false
|
2021-02-20 17:28:11 +00:00
|
|
|
super().__init__(challenge.data, encoder=DataclassEncoder, **kwargs)
|