stages/prompt: remove PolicyBindingModel from PromptStage *breaking*
This resolves issues caused by the multiple primary keys, but also requires re-creation of the model.
This commit is contained in:
parent
8db60b3e83
commit
1776b72356
|
@ -16,8 +16,6 @@ def create_default_password_change(apps: Apps, schema_editor: BaseDatabaseSchema
|
|||
Flow = apps.get_model("passbook_flows", "Flow")
|
||||
FlowStageBinding = apps.get_model("passbook_flows", "FlowStageBinding")
|
||||
|
||||
PolicyBinding = apps.get_model("passbook_policies", "PolicyBinding")
|
||||
|
||||
ExpressionPolicy = apps.get_model(
|
||||
"passbook_policies_expression", "ExpressionPolicy"
|
||||
)
|
||||
|
@ -58,17 +56,17 @@ def create_default_password_change(apps: Apps, schema_editor: BaseDatabaseSchema
|
|||
"order": 1,
|
||||
},
|
||||
)
|
||||
prompt_stage.fields.add(password_prompt)
|
||||
prompt_stage.fields.add(password_rep_prompt)
|
||||
|
||||
# Policy to only trigger prompt when no username is given
|
||||
prompt_policy, _ = ExpressionPolicy.objects.using(db_alias).update_or_create(
|
||||
name="default-password-change-password-equal",
|
||||
defaults={"expression": PROMPT_POLICY_EXPRESSION},
|
||||
)
|
||||
PolicyBinding.objects.using(db_alias).update_or_create(
|
||||
policy=prompt_policy, target=prompt_stage, defaults={"order": 0}
|
||||
)
|
||||
|
||||
prompt_stage.fields.add(password_prompt)
|
||||
prompt_stage.fields.add(password_rep_prompt)
|
||||
prompt_stage.validation_policies.add(prompt_policy)
|
||||
prompt_stage.save()
|
||||
|
||||
user_write, _ = UserWriteStage.objects.using(db_alias).update_or_create(
|
||||
name="default-password-change-write"
|
||||
|
@ -103,9 +101,8 @@ class Migration(migrations.Migration):
|
|||
dependencies = [
|
||||
("passbook_flows", "0006_auto_20200629_0857"),
|
||||
("passbook_policies_expression", "0001_initial"),
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_stages_password", "0001_initial"),
|
||||
("passbook_stages_prompt", "0004_auto_20200618_1735"),
|
||||
("passbook_stages_prompt", "0001_initial"),
|
||||
("passbook_stages_user_write", "0001_initial"),
|
||||
]
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ class PromptStageSerializer(ModelSerializer):
|
|||
"pk",
|
||||
"name",
|
||||
"fields",
|
||||
"validation_policies",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
"""Prompt forms"""
|
||||
from typing import Callable
|
||||
from email.policy import Policy
|
||||
from typing import Callable, Iterator, List
|
||||
|
||||
from django import forms
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.http import HttpRequest
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from guardian.shortcuts import get_anonymous_user
|
||||
|
||||
from passbook.core.models import User
|
||||
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||
from passbook.policies.engine import PolicyEngine
|
||||
from passbook.policies.models import PolicyBinding, PolicyBindingModel
|
||||
from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
||||
|
||||
|
||||
|
@ -18,7 +21,7 @@ class PromptStageForm(forms.ModelForm):
|
|||
class Meta:
|
||||
|
||||
model = PromptStage
|
||||
fields = ["name", "fields"]
|
||||
fields = ["name", "fields", "validation_policies"]
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
||||
"fields": FilteredSelectMultiple(_("prompts"), False),
|
||||
|
@ -45,6 +48,23 @@ class PromptAdminForm(forms.ModelForm):
|
|||
}
|
||||
|
||||
|
||||
class ListPolicyEngine(PolicyEngine):
|
||||
"""Slightly modified policy engine, which uses a list instead of a PolicyBindingModel"""
|
||||
|
||||
__list: List[Policy]
|
||||
|
||||
def __init__(
|
||||
self, policies: List[Policy], user: User, request: HttpRequest = None
|
||||
) -> None:
|
||||
super().__init__(PolicyBindingModel(), user, request)
|
||||
self.__list = policies
|
||||
self.use_cache = False
|
||||
|
||||
def _iter_bindings(self) -> Iterator[PolicyBinding]:
|
||||
for policy in self.__list:
|
||||
yield PolicyBinding(policy=policy,)
|
||||
|
||||
|
||||
class PromptForm(forms.Form):
|
||||
"""Dynamically created form based on PromptStage"""
|
||||
|
||||
|
@ -73,7 +93,7 @@ class PromptForm(forms.Form):
|
|||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
user = self.plan.context.get(PLAN_CONTEXT_PENDING_USER, get_anonymous_user())
|
||||
engine = PolicyEngine(self.stage, user)
|
||||
engine = ListPolicyEngine(self.stage.validation_policies.all(), user)
|
||||
engine.request.context = cleaned_data
|
||||
engine.build()
|
||||
result = engine.result
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.0.6 on 2020-05-19 22:08
|
||||
# Generated by Django 3.1.1 on 2020-09-09 08:40
|
||||
|
||||
import uuid
|
||||
|
||||
|
@ -11,8 +11,8 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("passbook_policies", "0001_initial"),
|
||||
("passbook_flows", "0001_initial"),
|
||||
("passbook_flows", "0007_auto_20200703_2059"),
|
||||
("passbook_policies", "0003_auto_20200908_1542"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -39,17 +39,30 @@ class Migration(migrations.Migration):
|
|||
"type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("text", "Text"),
|
||||
("e-mail", "Email"),
|
||||
("text", "Text: Simple Text input"),
|
||||
(
|
||||
"username",
|
||||
"Username: Same as Text input, but checks for and prevents duplicate usernames.",
|
||||
),
|
||||
("email", "Email: Text field with Email type."),
|
||||
("password", "Password"),
|
||||
("number", "Number"),
|
||||
("hidden", "Hidden"),
|
||||
("checkbox", "Checkbox"),
|
||||
("data", "Date"),
|
||||
("data-time", "Date Time"),
|
||||
("separator", "Separator: Static Separator Line"),
|
||||
(
|
||||
"hidden",
|
||||
"Hidden: Hidden field, can be used to insert data into form.",
|
||||
),
|
||||
("static", "Static: Static value, displayed as-is."),
|
||||
],
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
("required", models.BooleanField(default=True)),
|
||||
("placeholder", models.TextField()),
|
||||
("placeholder", models.TextField(blank=True)),
|
||||
("order", models.IntegerField(default=0)),
|
||||
],
|
||||
options={"verbose_name": "Prompt", "verbose_name_plural": "Prompts",},
|
||||
),
|
||||
|
@ -58,30 +71,25 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
(
|
||||
"stage_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
to="passbook_flows.Stage",
|
||||
),
|
||||
),
|
||||
(
|
||||
"policybindingmodel_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_policies.PolicyBindingModel",
|
||||
to="passbook_flows.stage",
|
||||
),
|
||||
),
|
||||
("fields", models.ManyToManyField(to="passbook_stages_prompt.Prompt")),
|
||||
(
|
||||
"validation_policies",
|
||||
models.ManyToManyField(blank=True, to="passbook_policies.Policy"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Prompt Stage",
|
||||
"verbose_name_plural": "Prompt Stages",
|
||||
},
|
||||
bases=("passbook_policies.policybindingmodel", "passbook_flows.stage"),
|
||||
bases=("passbook_flows.stage",),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
# Generated by Django 3.0.6 on 2020-05-28 20:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_stages_prompt", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="prompt", name="order", field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="prompt",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("text", "Text"),
|
||||
("e-mail", "Email"),
|
||||
("password", "Password"),
|
||||
("number", "Number"),
|
||||
("checkbox", "Checkbox"),
|
||||
("data", "Date"),
|
||||
("data-time", "Date Time"),
|
||||
("separator", "Separator"),
|
||||
("hidden", "Hidden"),
|
||||
("static", "Static"),
|
||||
],
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,33 +0,0 @@
|
|||
# Generated by Django 3.0.7 on 2020-06-15 16:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_stages_prompt", "0002_auto_20200528_2059"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="prompt",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("text", "Text"),
|
||||
("username", "Username"),
|
||||
("e-mail", "Email"),
|
||||
("password", "Password"),
|
||||
("number", "Number"),
|
||||
("checkbox", "Checkbox"),
|
||||
("data", "Date"),
|
||||
("data-time", "Date Time"),
|
||||
("separator", "Separator"),
|
||||
("hidden", "Hidden"),
|
||||
("static", "Static"),
|
||||
],
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,33 +0,0 @@
|
|||
# Generated by Django 3.0.7 on 2020-06-18 17:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_stages_prompt", "0003_auto_20200615_1641"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="prompt",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("text", "Text"),
|
||||
("username", "Username"),
|
||||
("email", "Email"),
|
||||
("password", "Password"),
|
||||
("number", "Number"),
|
||||
("checkbox", "Checkbox"),
|
||||
("data", "Date"),
|
||||
("data-time", "Date Time"),
|
||||
("separator", "Separator"),
|
||||
("hidden", "Hidden"),
|
||||
("static", "Static"),
|
||||
],
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,39 +0,0 @@
|
|||
# Generated by Django 3.0.8 on 2020-07-09 16:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_stages_prompt", "0004_auto_20200618_1735"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="prompt",
|
||||
name="type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("text", "Text: Simple Text input"),
|
||||
(
|
||||
"username",
|
||||
"Username: Same as Text input, but checks for and prevents duplicate usernames.",
|
||||
),
|
||||
("email", "Email: Text field with Email type."),
|
||||
("password", "Password"),
|
||||
("number", "Number"),
|
||||
("checkbox", "Checkbox"),
|
||||
("data", "Date"),
|
||||
("data-time", "Date Time"),
|
||||
("separator", "Separator: Static Separator Line"),
|
||||
(
|
||||
"hidden",
|
||||
"Hidden: Hidden field, can be used to insert data into form.",
|
||||
),
|
||||
("static", "Static: Static value, displayed as-is."),
|
||||
],
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,16 +0,0 @@
|
|||
# Generated by Django 3.1 on 2020-08-23 22:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_stages_prompt", "0005_auto_20200709_1608"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="prompt", name="placeholder", field=models.TextField(blank=True),
|
||||
),
|
||||
]
|
|
@ -11,7 +11,7 @@ from rest_framework.serializers import BaseSerializer
|
|||
|
||||
from passbook.flows.models import Stage
|
||||
from passbook.lib.models import SerializerModel
|
||||
from passbook.policies.models import PolicyBindingModel
|
||||
from passbook.policies.models import Policy
|
||||
from passbook.stages.prompt.widgets import HorizontalRuleWidget, StaticTextWidget
|
||||
|
||||
|
||||
|
@ -123,11 +123,13 @@ class Prompt(SerializerModel):
|
|||
verbose_name_plural = _("Prompts")
|
||||
|
||||
|
||||
class PromptStage(PolicyBindingModel, Stage):
|
||||
class PromptStage(Stage):
|
||||
"""Define arbitrary prompts for the user."""
|
||||
|
||||
fields = models.ManyToManyField(Prompt)
|
||||
|
||||
validation_policies = models.ManyToManyField(Policy, blank=True)
|
||||
|
||||
@property
|
||||
def serializer(self) -> BaseSerializer:
|
||||
from passbook.stages.prompt.api import PromptStageSerializer
|
||||
|
|
|
@ -11,7 +11,6 @@ from passbook.flows.models import Flow, FlowDesignation, FlowStageBinding
|
|||
from passbook.flows.planner import FlowPlan
|
||||
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.models import FieldTypes, Prompt, PromptStage
|
||||
from passbook.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
|
@ -124,7 +123,8 @@ class TestPromptStage(TestCase):
|
|||
expr_policy = ExpressionPolicy.objects.create(
|
||||
name="validate-form", expression=expr
|
||||
)
|
||||
PolicyBinding.objects.create(policy=expr_policy, target=self.stage, order=0)
|
||||
self.stage.validation_policies.set([expr_policy])
|
||||
self.stage.save()
|
||||
form = PromptForm(stage=self.stage, plan=plan, data=self.prompt_data)
|
||||
self.assertEqual(form.is_valid(), True)
|
||||
return form
|
||||
|
@ -138,7 +138,8 @@ class TestPromptStage(TestCase):
|
|||
expr_policy = ExpressionPolicy.objects.create(
|
||||
name="validate-form", expression=expr
|
||||
)
|
||||
PolicyBinding.objects.create(policy=expr_policy, target=self.stage, order=0)
|
||||
self.stage.validation_policies.set([expr_policy])
|
||||
self.stage.save()
|
||||
form = PromptForm(stage=self.stage, plan=plan, data=self.prompt_data)
|
||||
self.assertEqual(form.is_valid(), False)
|
||||
return form
|
||||
|
|
Reference in a new issue