flows: add check if current plan matches current flow
This commit is contained in:
parent
a7567ad8c6
commit
99bab03cce
|
@ -21,6 +21,7 @@ class FlowPlan:
|
|||
"""This data-class is the output of a FlowPlanner. It holds a flat list
|
||||
of all Stages that should be run."""
|
||||
|
||||
flow_pk: str
|
||||
stages: List[Stage] = field(default_factory=list)
|
||||
context: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
@ -46,9 +47,9 @@ class FlowPlanner:
|
|||
def plan(self, request: HttpRequest) -> FlowPlan:
|
||||
"""Check each of the flows' policies, check policies for each stage with PolicyBinding
|
||||
and return ordered list"""
|
||||
LOGGER.debug("Starting planning process", flow=self.flow)
|
||||
LOGGER.debug("f(plan): Starting planning process", flow=self.flow)
|
||||
start_time = time()
|
||||
plan = FlowPlan()
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex)
|
||||
# First off, check the flow's direct policy bindings
|
||||
# to make sure the user even has access to the flow
|
||||
root_passing, root_passing_messages = self._check_flow_root_policies(request)
|
||||
|
@ -65,11 +66,13 @@ class FlowPlanner:
|
|||
engine.build()
|
||||
passing, _ = engine.result
|
||||
if passing:
|
||||
LOGGER.debug("Stage passing", stage=stage)
|
||||
LOGGER.debug("f(plan): Stage passing", stage=stage)
|
||||
plan.stages.append(stage)
|
||||
end_time = time()
|
||||
LOGGER.debug(
|
||||
"Finished planning", flow=self.flow, duration_s=end_time - start_time
|
||||
"f(plan): Finished planning",
|
||||
flow=self.flow,
|
||||
duration_s=end_time - start_time,
|
||||
)
|
||||
if not plan.stages:
|
||||
raise EmptyFlowException()
|
||||
|
|
|
@ -63,7 +63,20 @@ class FlowExecutorView(View):
|
|||
|
||||
def dispatch(self, request: HttpRequest, flow_slug: str) -> HttpResponse:
|
||||
# Early check if theres an active Plan for the current session
|
||||
if SESSION_KEY_PLAN not in self.request.session:
|
||||
if SESSION_KEY_PLAN in self.request.session:
|
||||
self.plan = self.request.session[SESSION_KEY_PLAN]
|
||||
if self.plan.flow_pk != self.flow.pk.hex:
|
||||
LOGGER.warning(
|
||||
"f(exec): Found existing plan for other flow, deleteing plan",
|
||||
flow_slug=flow_slug,
|
||||
)
|
||||
# Existing plan is deleted from session and instance
|
||||
self.plan = None
|
||||
self.cancel()
|
||||
LOGGER.debug("f(exec): Continuing existing plan", flow_slug=flow_slug)
|
||||
|
||||
# Don't check session again as we've either already loaded the plan or we need to plan
|
||||
if not self.plan:
|
||||
LOGGER.debug(
|
||||
"f(exec): No active Plan found, initiating planner", flow_slug=flow_slug
|
||||
)
|
||||
|
@ -75,9 +88,6 @@ class FlowExecutorView(View):
|
|||
except EmptyFlowException as exc:
|
||||
LOGGER.warning("f(exec): Flow is empty", exc=exc)
|
||||
return self.handle_invalid_flow(exc)
|
||||
else:
|
||||
LOGGER.debug("f(exec): Continuing existing plan", flow_slug=flow_slug)
|
||||
self.plan = self.request.session[SESSION_KEY_PLAN]
|
||||
# We don't save the Plan after getting the next stage
|
||||
# as it hasn't been successfully passed yet
|
||||
self.current_stage = self.plan.next()
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# Generated by Django 3.0.5 on 2020-05-10 16:48
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_stages_password", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(model_name="passwordstage", name="password_policies",),
|
||||
]
|
|
@ -42,7 +42,7 @@ class TestPasswordStage(TestCase):
|
|||
|
||||
def test_without_user(self):
|
||||
"""Test without user"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
|
@ -59,7 +59,7 @@ class TestPasswordStage(TestCase):
|
|||
|
||||
def test_valid_password(self):
|
||||
"""Test with a valid pending user and valid password"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
|
@ -77,7 +77,7 @@ class TestPasswordStage(TestCase):
|
|||
|
||||
def test_invalid_password(self):
|
||||
"""Test with a valid pending user and invalid password"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
|
@ -99,9 +99,7 @@ class TestPasswordStage(TestCase):
|
|||
def test_permission_denied(self):
|
||||
"""Test with a valid pending user and valid password.
|
||||
Backend is patched to return PermissionError"""
|
||||
# from django.contrib.auth.backends import ModelBackend
|
||||
# ModelBackend().authenticate()
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 3.0.5 on 2020-05-10 16:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_stages_prompt", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="prompt",
|
||||
options={"verbose_name": "Prompt", "verbose_name_plural": "Prompts"},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="prompt",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("text", "Text"),
|
||||
("e-mail", "Email"),
|
||||
("password", "Password"),
|
||||
("number", "Number"),
|
||||
("hidden", "Hidden"),
|
||||
],
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -97,7 +97,7 @@ class TestPromptStage(TestCase):
|
|||
|
||||
def test_render(self):
|
||||
"""Test render of form, check if all prompts are rendered correctly"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
|
@ -121,7 +121,7 @@ class TestPromptStage(TestCase):
|
|||
|
||||
def test_valid_form_request(self):
|
||||
"""Test a request with valid form data"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
|
|
|
@ -34,7 +34,7 @@ class TestUserCreateStage(TestCase):
|
|||
|
||||
def test_valid_create(self):
|
||||
"""Test creation of user"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
plan.context[PLAN_CONTEXT_PROMPT] = {
|
||||
"username": "test-user",
|
||||
"name": "name",
|
||||
|
@ -59,7 +59,7 @@ class TestUserCreateStage(TestCase):
|
|||
|
||||
def test_without_data(self):
|
||||
"""Test without data results in error"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
|
|
|
@ -29,7 +29,7 @@ class TestUserLoginStage(TestCase):
|
|||
|
||||
def test_valid_password(self):
|
||||
"""Test with a valid pending user and backend"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||
plan.context[
|
||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||
|
@ -48,7 +48,7 @@ class TestUserLoginStage(TestCase):
|
|||
|
||||
def test_without_user(self):
|
||||
"""Test a plan without any pending user, resulting in a denied"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
|
@ -63,7 +63,7 @@ class TestUserLoginStage(TestCase):
|
|||
|
||||
def test_without_backend(self):
|
||||
"""Test a plan with pending user, without backend, resulting in a denied"""
|
||||
plan = FlowPlan(stages=[self.stage])
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage])
|
||||
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
|
|
Reference in New Issue