stages/prompt: add policy verification logic

This commit is contained in:
Jens Langhammer 2020-05-14 15:48:23 +02:00
parent 776ad3cfbf
commit 814c797c64
3 changed files with 56 additions and 3 deletions

View File

@ -1,6 +1,9 @@
"""Prompt forms""" """Prompt forms"""
from django import forms from django import forms
from guardian.shortcuts import get_anonymous_user
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from passbook.policies.engine import PolicyEngine
from passbook.stages.prompt.models import Prompt, PromptStage from passbook.stages.prompt.models import Prompt, PromptStage
@ -20,10 +23,22 @@ class PromptForm(forms.Form):
"""Dynamically created form based on PromptStage""" """Dynamically created form based on PromptStage"""
stage: PromptStage stage: PromptStage
plan: FlowPlan
def __init__(self, stage: PromptStage, *args, **kwargs): def __init__(self, stage: PromptStage, plan: FlowPlan, *args, **kwargs):
self.stage = stage self.stage = stage
self.plan = plan
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
for field in self.stage.fields.all(): for field in self.stage.fields.all():
field: Prompt field: Prompt
self.fields[field.field_key] = field.field self.fields[field.field_key] = field.field
def clean(self):
cleaned_data = super().clean()
user = self.plan.context.get(PLAN_CONTEXT_PENDING_USER, get_anonymous_user())
engine = PolicyEngine(self.stage.policies.all(), user)
engine.request.context = cleaned_data
engine.build()
passing, messages = engine.result
if not passing:
raise forms.ValidationError(messages)

View File

@ -25,6 +25,7 @@ class PromptStageView(FormView, AuthenticationStage):
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
kwargs["stage"] = self.executor.current_stage kwargs["stage"] = self.executor.current_stage
kwargs["plan"] = self.executor.plan
return kwargs return kwargs
def form_valid(self, form: PromptForm) -> HttpResponse: def form_valid(self, form: PromptForm) -> HttpResponse:

View File

@ -8,6 +8,8 @@ from passbook.core.models import User
from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
from passbook.flows.planner import FlowPlan from passbook.flows.planner import FlowPlan
from passbook.flows.views import SESSION_KEY_PLAN from passbook.flows.views import SESSION_KEY_PLAN
from passbook.policies.expression.models import ExpressionPolicy
from passbook.policies.models import PolicyBinding
from passbook.stages.prompt.forms import PromptForm from passbook.stages.prompt.forms import PromptForm
from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage
from passbook.stages.prompt.stage import PLAN_CONTEXT_PROMPT from passbook.stages.prompt.stage import PLAN_CONTEXT_PROMPT
@ -47,6 +49,13 @@ class TestPromptStage(TestCase):
required=True, required=True,
placeholder="PASSWORD_PLACEHOLDER", placeholder="PASSWORD_PLACEHOLDER",
) )
password2_prompt = Prompt.objects.create(
field_key="password2_prompt",
label="PASSWORD_LABEL",
type=FieldTypes.PASSWORD,
required=True,
placeholder="PASSWORD_PLACEHOLDER",
)
number_prompt = Prompt.objects.create( number_prompt = Prompt.objects.create(
field_key="number_prompt", field_key="number_prompt",
label="NUMBER_LABEL", label="NUMBER_LABEL",
@ -62,7 +71,14 @@ class TestPromptStage(TestCase):
) )
self.stage = PromptStage.objects.create(name="prompt-stage") self.stage = PromptStage.objects.create(name="prompt-stage")
self.stage.fields.set( self.stage.fields.set(
[text_prompt, email_prompt, password_prompt, number_prompt, hidden_prompt,] [
text_prompt,
email_prompt,
password_prompt,
password2_prompt,
number_prompt,
hidden_prompt,
]
) )
self.stage.save() self.stage.save()
@ -70,6 +86,7 @@ class TestPromptStage(TestCase):
text_prompt.field_key: "test-input", text_prompt.field_key: "test-input",
email_prompt.field_key: "test@test.test", email_prompt.field_key: "test@test.test",
password_prompt.field_key: "test", password_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,
} }
@ -115,10 +132,30 @@ class TestPromptStage(TestCase):
def test_valid_form(self) -> PromptForm: def test_valid_form(self) -> PromptForm:
"""Test form validation""" """Test form validation"""
form = PromptForm(stage=self.stage, data=self.prompt_data) plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
expr = (
"{{ request.context.password_prompt == request.context.password2_prompt }}"
)
expr_policy = ExpressionPolicy.objects.create(
name="validate-form", expression=expr
)
PolicyBinding.objects.create(policy=expr_policy, target=self.stage)
form = PromptForm(stage=self.stage, plan=plan, data=self.prompt_data)
self.assertEqual(form.is_valid(), True) self.assertEqual(form.is_valid(), True)
return form return form
def test_invalid_form(self) -> PromptForm:
"""Test form validation"""
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
expr = "False"
expr_policy = ExpressionPolicy.objects.create(
name="validate-form", expression=expr
)
PolicyBinding.objects.create(policy=expr_policy, target=self.stage)
form = PromptForm(stage=self.stage, plan=plan, data=self.prompt_data)
self.assertEqual(form.is_valid(), False)
return form
def test_valid_form_request(self): def test_valid_form_request(self):
"""Test a request with valid form data""" """Test a request with valid form data"""
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage]) plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])