stages/prompt: add dry-run

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer 2023-06-14 11:07:16 +02:00
parent 17d069dd45
commit 4a6efc338e
No known key found for this signature in database
2 changed files with 65 additions and 8 deletions

View File

@ -19,11 +19,12 @@ from rest_framework.serializers import ValidationError
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import User
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes, HttpChallengeResponse
from authentik.flows.planner import FlowPlan
from authentik.flows.stage import ChallengeStageView
from authentik.policies.engine import PolicyEngine
from authentik.policies.models import PolicyBinding, PolicyBindingModel, PolicyEngineMode
from authentik.policies.types import PolicyResult
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
from authentik.stages.prompt.signals import password_validate
@ -33,7 +34,7 @@ PLAN_CONTEXT_PROMPT = "prompt_data"
class StagePromptSerializer(PassiveSerializer):
"""Serializer for a single Prompt field"""
field_key = CharField()
field_key = CharField(required=True)
label = CharField(allow_blank=True)
type = ChoiceField(choices=FieldTypes.choices)
required = BooleanField()
@ -44,21 +45,39 @@ class StagePromptSerializer(PassiveSerializer):
choices = ListField(child=CharField(allow_blank=True), allow_empty=True, allow_null=True)
class PromptChallengeMeta(PassiveSerializer):
"""Additional context sent with the initial challenge, which might contain
info when doing dry-runs or other validation fails"""
field_key = CharField(required=True)
class PromptChallenge(Challenge):
"""Initial challenge being sent, define fields"""
fields = StagePromptSerializer(many=True)
meta = PromptChallengeMeta(many=True)
component = CharField(default="ak-stage-prompt")
class PromptChallengeResponseMeta(PassiveSerializer):
"""Additional context sent back by the flow executor when submitting
the prompt stage"""
dry_run = BooleanField(required=True)
class PromptChallengeResponse(ChallengeResponse):
"""Validate response, fields are dynamically created based
on the stage"""
stage_instance: PromptStage
validation_result: PolicyResult
component = CharField(default="ak-stage-prompt")
meta = PromptChallengeResponseMeta()
def __init__(self, *args, **kwargs):
stage: PromptStage = kwargs.pop("stage_instance", None)
plan: FlowPlan = kwargs.pop("plan", None)
@ -142,9 +161,9 @@ class PromptChallengeResponse(ChallengeResponse):
engine.request.context[PLAN_CONTEXT_PROMPT] = attrs
engine.use_cache = False
engine.build()
result = engine.result
if not result.passing:
raise ValidationError(list(result.messages))
self.validation_result = engine.result
if not self.validation_result.passing:
raise ValidationError(list(self.validation_result.messages))
return attrs
@ -240,8 +259,17 @@ class PromptStageView(ChallengeStageView):
user=self.get_pending_user(),
)
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:
self.executor.plan.context[PLAN_CONTEXT_PROMPT] = {}
def challenge_dry_run(self, response: PromptChallengeResponse, result: PolicyResult) -> HttpResponse:
challenge = self.get_challenge()
# TODO update challenge.meta
return HttpChallengeResponse(challenge)
def challenge_valid(self, response: PromptChallengeResponse) -> HttpResponse:
if response.validated_data["meta"]["dry_run"]:
# If we get to this point, the serializer must have a .validation_result attribute
# as if any other validation fails, it would've raised a validation error
# which is handled in the challenge stage base class
return self.challenge_dry_run(response, response.validation_result)
self.executor.plan.context.setdefault(PLAN_CONTEXT_PROMPT, {})
self.executor.plan.context[PLAN_CONTEXT_PROMPT].update(response.validated_data)
return self.executor.stage_ok()

View File

@ -38593,9 +38593,34 @@ components:
type: array
items:
$ref: '#/components/schemas/StagePrompt'
meta:
type: array
items:
$ref: '#/components/schemas/PromptChallengeMeta'
required:
- fields
- meta
- type
PromptChallengeMeta:
type: object
description: |-
Additional context sent with the initial challenge, which might contain
info when doing dry-runs or other validation fails
properties:
field_key:
type: string
required:
- field_key
PromptChallengeResponseMetaRequest:
type: object
description: |-
Additional context sent back by the flow executor when submitting
the prompt stage
properties:
dry_run:
type: boolean
required:
- dry_run
PromptChallengeResponseRequest:
type: object
description: |-
@ -38606,6 +38631,10 @@ components:
type: string
minLength: 1
default: ak-stage-prompt
meta:
$ref: '#/components/schemas/PromptChallengeResponseMetaRequest'
required:
- meta
additionalProperties: {}
PromptRequest:
type: object