flows: update migrations to use update_or_create

This commit is contained in:
Jens Langhammer 2020-06-29 16:19:39 +02:00
parent b8654c06bf
commit ec823aebed
6 changed files with 97 additions and 87 deletions

View File

@ -20,42 +20,38 @@ def create_default_authentication_flow(
) )
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
if ( identification_stage, _ = IdentificationStage.objects.using(
Flow.objects.using(db_alias) db_alias
.filter(designation=FlowDesignation.AUTHENTICATION) ).update_or_create(
.exists() name="default-authentication-identification",
): defaults={
# Only create default flow when none exist "user_fields": [UserFields.E_MAIL, UserFields.USERNAME],
return "template": Templates.DEFAULT_LOGIN,
},
if not IdentificationStage.objects.using(db_alias).exists():
IdentificationStage.objects.using(db_alias).create(
name="identification",
user_fields=[UserFields.E_MAIL, UserFields.USERNAME],
template=Templates.DEFAULT_LOGIN,
) )
if not PasswordStage.objects.using(db_alias).exists(): password_stage, _ = PasswordStage.objects.using(db_alias).update_or_create(
PasswordStage.objects.using(db_alias).create( name="default-authentication-password",
name="password", backends=["django.contrib.auth.backends.ModelBackend"], defaults={"backends": ["django.contrib.auth.backends.ModelBackend"]},
) )
if not UserLoginStage.objects.using(db_alias).exists(): login_stage, _ = UserLoginStage.objects.using(db_alias).update_or_create(
UserLoginStage.objects.using(db_alias).create(name="authentication") name="default-authentication-login"
)
flow = Flow.objects.using(db_alias).create( flow, _ = Flow.objects.using(db_alias).update_or_create(
name="Welcome to passbook!",
slug="default-authentication-flow", slug="default-authentication-flow",
designation=FlowDesignation.AUTHENTICATION, designation=FlowDesignation.AUTHENTICATION,
defaults={"name": "Welcome to passbook!",},
) )
FlowStageBinding.objects.using(db_alias).create( FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=IdentificationStage.objects.using(db_alias).first(), order=0, flow=flow, stage=identification_stage, defaults={"order": 0,},
) )
FlowStageBinding.objects.using(db_alias).create( FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=PasswordStage.objects.using(db_alias).first(), order=1, flow=flow, stage=password_stage, defaults={"order": 1,},
) )
FlowStageBinding.objects.using(db_alias).create( FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=UserLoginStage.objects.using(db_alias).first(), order=2, flow=flow, stage=login_stage, defaults={"order": 2,},
) )
@ -67,24 +63,19 @@ def create_default_invalidation_flow(
UserLogoutStage = apps.get_model("passbook_stages_user_logout", "UserLogoutStage") UserLogoutStage = apps.get_model("passbook_stages_user_logout", "UserLogoutStage")
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
if ( UserLogoutStage.objects.using(db_alias).update_or_create(
Flow.objects.using(db_alias) name="default-invalidation-logout"
.filter(designation=FlowDesignation.INVALIDATION) )
.exists()
):
# Only create default flow when none exist
return
if not UserLogoutStage.objects.using(db_alias).exists(): flow, _ = Flow.objects.using(db_alias).update_or_create(
UserLogoutStage.objects.using(db_alias).create(name="logout")
flow = Flow.objects.using(db_alias).create(
name="default-invalidation-flow",
slug="default-invalidation-flow", slug="default-invalidation-flow",
designation=FlowDesignation.INVALIDATION, designation=FlowDesignation.INVALIDATION,
defaults={"name": "Logout",},
) )
FlowStageBinding.objects.using(db_alias).create( FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=UserLogoutStage.objects.using(db_alias).first(), order=0, flow=flow,
stage=UserLogoutStage.objects.using(db_alias).first(),
defaults={"order": 0,},
) )

View File

@ -34,60 +34,63 @@ def create_default_source_enrollment_flow(
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
# Create a policy that only allows this flow when doing an SSO Request # Create a policy that only allows this flow when doing an SSO Request
flow_policy = ExpressionPolicy.objects.using(db_alias).create( flow_policy, _ = ExpressionPolicy.objects.using(db_alias).update_or_create(
name="default-source-enrollment-if-sso", expression=FLOW_POLICY_EXPRESSION name="default-source-enrollment-if-sso",
defaults={"expression": FLOW_POLICY_EXPRESSION},
) )
# This creates a Flow used by sources to enroll users # This creates a Flow used by sources to enroll users
# It makes sure that a username is set, and if not, prompts the user for a Username # It makes sure that a username is set, and if not, prompts the user for a Username
flow = Flow.objects.using(db_alias).create( flow, _ = Flow.objects.using(db_alias).update_or_create(
name="default-source-enrollment",
slug="default-source-enrollment", slug="default-source-enrollment",
designation=FlowDesignation.ENROLLMENT, designation=FlowDesignation.ENROLLMENT,
defaults={"name": "Welcome to passbook!",},
) )
PolicyBinding.objects.using(db_alias).create( PolicyBinding.objects.using(db_alias).update_or_create(
policy=flow_policy, target=flow, order=0 policy=flow_policy, target=flow, defaults={"order": 0}
) )
# PromptStage to ask user for their username # PromptStage to ask user for their username
prompt_stage = PromptStage.objects.using(db_alias).create( prompt_stage, _ = PromptStage.objects.using(db_alias).update_or_create(
name="default-source-enrollment-username-prompt", name="default-source-enrollment-username-prompt",
) )
prompt_stage.fields.add( prompt, _ = Prompt.objects.using(db_alias).update_or_create(
Prompt.objects.using(db_alias).create(
field_key="username", field_key="username",
label="Username", defaults={
type=FieldTypes.TEXT, "label": "Username",
required=True, "type": FieldTypes.TEXT,
placeholder="Username", "required": True,
) "placeholder": "Username",
},
) )
prompt_stage.fields.add(prompt)
# Policy to only trigger prompt when no username is given # Policy to only trigger prompt when no username is given
prompt_policy = ExpressionPolicy.objects.using(db_alias).create( prompt_policy, _ = ExpressionPolicy.objects.using(db_alias).update_or_create(
name="default-source-enrollment-if-username", name="default-source-enrollment-if-username",
expression=PROMPT_POLICY_EXPRESSION, defaults={"expression": PROMPT_POLICY_EXPRESSION},
) )
# UserWrite stage to create the user, and login stage to log user in # UserWrite stage to create the user, and login stage to log user in
user_write = UserWriteStage.objects.using(db_alias).create( user_write, _ = UserWriteStage.objects.using(db_alias).update_or_create(
name="default-source-enrollment-write" name="default-source-enrollment-write"
) )
user_login = UserLoginStage.objects.using(db_alias).create( user_login, _ = UserLoginStage.objects.using(db_alias).update_or_create(
name="default-source-enrollment-login" name="default-source-enrollment-login"
) )
binding = FlowStageBinding.objects.using(db_alias).create( binding, _ = FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=prompt_stage, order=0 flow=flow, stage=prompt_stage, defaults={"order": 0}
) )
PolicyBinding.objects.using(db_alias).create( PolicyBinding.objects.using(db_alias).update_or_create(
policy=prompt_policy, target=binding, order=0 policy=prompt_policy, target=binding, defaults={"order": 0}
) )
FlowStageBinding.objects.using(db_alias).create( FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=user_write, order=1 flow=flow, stage=user_write, defaults={"order": 1}
) )
FlowStageBinding.objects.using(db_alias).create( FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=user_login, order=2 flow=flow, stage=user_login, defaults={"order": 2}
) )
@ -107,25 +110,26 @@ def create_default_source_authentication_flow(
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
# Create a policy that only allows this flow when doing an SSO Request # Create a policy that only allows this flow when doing an SSO Request
flow_policy = ExpressionPolicy.objects.using(db_alias).create( flow_policy, _ = ExpressionPolicy.objects.using(db_alias).update_or_create(
name="default-source-authentication-if-sso", expression=FLOW_POLICY_EXPRESSION name="default-source-authentication-if-sso",
defaults={"expression": FLOW_POLICY_EXPRESSION,},
) )
# This creates a Flow used by sources to authenticate users # This creates a Flow used by sources to authenticate users
flow = Flow.objects.using(db_alias).create( flow, _ = Flow.objects.using(db_alias).update_or_create(
name="default-source-authentication",
slug="default-source-authentication", slug="default-source-authentication",
designation=FlowDesignation.AUTHENTICATION, designation=FlowDesignation.AUTHENTICATION,
defaults={"name": "Welcome to passbook!",},
) )
PolicyBinding.objects.using(db_alias).create( PolicyBinding.objects.using(db_alias).update_or_create(
policy=flow_policy, target=flow, order=0 policy=flow_policy, target=flow, defaults={"order": 0}
) )
user_login = UserLoginStage.objects.using(db_alias).create( user_login, _ = UserLoginStage.objects.using(db_alias).update_or_create(
name="default-source-authentication-login" name="default-source-authentication-login"
) )
FlowStageBinding.objects.using(db_alias).create( FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=user_login, order=0 flow=flow, stage=user_login, defaults={"order": 0}
) )

View File

@ -7,7 +7,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from passbook.flows.models import FlowDesignation from passbook.flows.models import FlowDesignation
def create_default_provider_authz_flow( def create_default_provider_authorization_flow(
apps: Apps, schema_editor: BaseDatabaseSchemaEditor apps: Apps, schema_editor: BaseDatabaseSchemaEditor
): ):
Flow = apps.get_model("passbook_flows", "Flow") Flow = apps.get_model("passbook_flows", "Flow")
@ -18,22 +18,24 @@ def create_default_provider_authz_flow(
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
# Empty flow for providers where consent is implicitly given # Empty flow for providers where consent is implicitly given
Flow.objects.using(db_alias).create( Flow.objects.using(db_alias).update_or_create(
name="Authorize Application",
slug="default-provider-authorization-implicit-consent", slug="default-provider-authorization-implicit-consent",
designation=FlowDesignation.AUTHORIZATION, designation=FlowDesignation.AUTHORIZATION,
defaults={"name": "Authorize Application"},
) )
# Flow with consent form to obtain explicit user consent # Flow with consent form to obtain explicit user consent
flow = Flow.objects.using(db_alias).create( flow, _ = Flow.objects.using(db_alias).update_or_create(
name="Authorize Application",
slug="default-provider-authorization-explicit-consent", slug="default-provider-authorization-explicit-consent",
designation=FlowDesignation.AUTHORIZATION, designation=FlowDesignation.AUTHORIZATION,
defaults={"name": "Authorize Application"},
) )
stage = ConsentStage.objects.using(db_alias).create( stage, _ = ConsentStage.objects.using(db_alias).update_or_create(
name="default-provider-authorization-consent" name="default-provider-authorization-consent"
) )
FlowStageBinding.objects.using(db_alias).create(flow=flow, stage=stage, order=0) FlowStageBinding.objects.using(db_alias).update_or_create(
flow=flow, stage=stage, defaults={"order": 0}
)
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -43,4 +45,4 @@ class Migration(migrations.Migration):
("passbook_stages_consent", "0001_initial"), ("passbook_stages_consent", "0001_initial"),
] ]
operations = [migrations.RunPython(create_default_provider_authz_flow)] operations = [migrations.RunPython(create_default_provider_authorization_flow)]

View File

@ -153,7 +153,7 @@ class Processor:
self, request: HttpRequest, flow: Flow, **kwargs self, request: HttpRequest, flow: Flow, **kwargs
) -> HttpResponse: ) -> HttpResponse:
kwargs[PLAN_CONTEXT_SSO] = True kwargs[PLAN_CONTEXT_SSO] = True
request.session[SESSION_KEY_PLAN] = FlowPlanner(flow).plan(request, kwargs,) request.session[SESSION_KEY_PLAN] = FlowPlanner(flow).plan(request, kwargs)
return redirect_with_qs( return redirect_with_qs(
"passbook_flows:flow-executor-shell", request.GET, flow_slug=flow.slug, "passbook_flows:flow-executor-shell", request.GET, flow_slug=flow.slug,
) )

View File

@ -5,6 +5,7 @@ from django.core.validators import validate_email
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from structlog import get_logger from structlog import get_logger
from passbook.flows.models import Flow, FlowDesignation
from passbook.lib.utils.ui import human_list from passbook.lib.utils.ui import human_list
from passbook.stages.identification.models import IdentificationStage, UserFields from passbook.stages.identification.models import IdentificationStage, UserFields
@ -14,6 +15,15 @@ LOGGER = get_logger()
class IdentificationStageForm(forms.ModelForm): class IdentificationStageForm(forms.ModelForm):
"""Form to create/edit IdentificationStage instances""" """Form to create/edit IdentificationStage instances"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["enrollment_flow"].queryset = Flow.objects.filter(
designation=FlowDesignation.ENROLLMENT
)
self.fields["recovery_flow"].queryset = Flow.objects.filter(
designation=FlowDesignation.RECOVERY
)
class Meta: class Meta:
model = IdentificationStage model = IdentificationStage

View File

@ -1,5 +1,7 @@
"""Prompt forms""" """Prompt forms"""
from django import forms from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext_lazy as _
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
@ -16,6 +18,7 @@ class PromptStageForm(forms.ModelForm):
fields = ["name", "fields"] fields = ["name", "fields"]
widgets = { widgets = {
"name": forms.TextInput(), "name": forms.TextInput(),
"fields": FilteredSelectMultiple(_("prompts"), False),
} }