flows/*: Initial flows stage1 implementation

This commit is contained in:
Jens Langhammer 2020-05-07 20:51:06 +02:00
parent 179f0097c0
commit 8de66b27ad
12 changed files with 317 additions and 1 deletions

View File

11
passbook/flows/apps.py Normal file
View File

@ -0,0 +1,11 @@
"""passbook flows app config"""
from django.apps import AppConfig
class PassbookFlowsConfig(AppConfig):
"""passbook flows app config"""
name = "passbook.flows"
label = "passbook_flows"
mountpoint = "flows/"
verbose_name = "passbook Flows"

View File

@ -0,0 +1,108 @@
# Generated by Django 3.0.3 on 2020-05-07 18:35
import uuid
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("passbook_policies", "0001_initial"),
("passbook_core", "0011_auto_20200222_1822"),
]
operations = [
migrations.CreateModel(
name="Flow",
fields=[
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("name", models.TextField()),
("slug", models.SlugField(unique=True)),
(
"designation",
models.CharField(
choices=[
("AUTHENTICATION", "authentication"),
("ENROLLMENT", "enrollment"),
("RECOVERY", "recovery"),
],
max_length=100,
),
),
(
"pbm",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
related_name="+",
to="passbook_policies.PolicyBindingModel",
),
),
],
options={"verbose_name": "Flow", "verbose_name_plural": "Flows",},
bases=("passbook_policies.policybindingmodel", models.Model),
),
migrations.CreateModel(
name="FlowFactorBinding",
fields=[
(
"policybindingmodel_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
to="passbook_policies.PolicyBindingModel",
),
),
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("order", models.IntegerField()),
(
"factor",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="passbook_core.Factor",
),
),
(
"flow",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="passbook_flows.Flow",
),
),
],
options={
"verbose_name": "Flow Factor Binding",
"verbose_name_plural": "Flow Factor Bindings",
"unique_together": {("flow", "factor", "order")},
},
bases=("passbook_policies.policybindingmodel", models.Model),
),
migrations.AddField(
model_name="flow",
name="factors",
field=models.ManyToManyField(
through="passbook_flows.FlowFactorBinding", to="passbook_core.Factor"
),
),
]

View File

71
passbook/flows/models.py Normal file
View File

@ -0,0 +1,71 @@
"""Flow models"""
from enum import Enum
from typing import Tuple
from django.db import models
from django.utils.translation import gettext_lazy as _
from passbook.core.models import Factor
from passbook.lib.models import UUIDModel
from passbook.policies.models import PolicyBindingModel
class FlowDesignation(Enum):
"""Designation of what a Flow should be used for. At a later point, this
should be replaced by a database entry."""
AUTHENTICATION = "authentication"
ENROLLMENT = "enrollment"
RECOVERY = "recovery"
@staticmethod
def as_choices() -> Tuple[Tuple[str, str]]:
"""Generate choices of actions used for database"""
return tuple(
(x, y.value) for x, y in getattr(FlowDesignation, "__members__").items()
)
class Flow(PolicyBindingModel, UUIDModel):
"""Flow describes how a series of Factors should be executed to authenticate/enroll/recover
a user. Additionally, policies can be applied, to specify which users
have access to this flow."""
name = models.TextField()
slug = models.SlugField(unique=True)
designation = models.CharField(max_length=100, choices=FlowDesignation.as_choices())
factors = models.ManyToManyField(Factor, through="FlowFactorBinding")
pbm = models.OneToOneField(
PolicyBindingModel, parent_link=True, on_delete=models.CASCADE, related_name="+"
)
def __str__(self) -> str:
return f"Flow {self.name} ({self.slug})"
class Meta:
verbose_name = _("Flow")
verbose_name_plural = _("Flows")
class FlowFactorBinding(PolicyBindingModel, UUIDModel):
"""Relationship between Flow and Factor. Order is required and unique for
each flow-factor Binding. Additionally, policies can be specified, which determine if
this Binding applies to the current user"""
flow = models.ForeignKey("Flow", on_delete=models.CASCADE)
factor = models.ForeignKey(Factor, on_delete=models.CASCADE)
order = models.IntegerField()
def __str__(self) -> str:
return f"Flow Factor Binding {self.flow} -> {self.factor}"
class Meta:
verbose_name = _("Flow Factor Binding")
verbose_name_plural = _("Flow Factor Bindings")
unique_together = (("flow", "factor", "order"),)

2
passbook/flows/urls.py Normal file
View File

@ -0,0 +1,2 @@
"""flow urls"""
urlpatterns = []

10
passbook/policies/apps.py Normal file
View File

@ -0,0 +1,10 @@
"""passbook policies app config"""
from django.apps import AppConfig
class PassbookPoliciesConfig(AppConfig):
"""passbook policies app config"""
name = "passbook.policies"
label = "passbook_policies"
verbose_name = "passbook Policies"

View File

@ -0,0 +1,77 @@
# Generated by Django 3.0.3 on 2020-05-07 18:35
import uuid
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("passbook_core", "0011_auto_20200222_1822"),
]
operations = [
migrations.CreateModel(
name="PolicyBinding",
fields=[
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("enabled", models.BooleanField(default=True)),
("order", models.IntegerField(default=0)),
(
"policy",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="passbook_core.Policy",
),
),
],
options={
"verbose_name": "Policy Binding",
"verbose_name_plural": "Policy Bindings",
},
),
migrations.CreateModel(
name="PolicyBindingModel",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"policies",
models.ManyToManyField(
related_name="_policybindingmodel_policies_+",
through="passbook_policies.PolicyBinding",
to="passbook_core.Policy",
),
),
],
),
migrations.AddField(
model_name="policybinding",
name="target",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="passbook_policies.PolicyBindingModel",
),
),
]

View File

View File

@ -0,0 +1,31 @@
"""Policy base models"""
from django.db import models
from django.utils.translation import gettext_lazy as _
from passbook.core.models import Policy
from passbook.lib.models import UUIDModel
class PolicyBindingModel(models.Model):
"""Base Model for objects which have Policies applied to them"""
policies = models.ManyToManyField(Policy, through="PolicyBinding", related_name="+")
class PolicyBinding(UUIDModel):
"""Relationship between a Policy and a PolicyBindingModel."""
enabled = models.BooleanField(default=True)
policy = models.ForeignKey(Policy, on_delete=models.CASCADE, related_name="+")
target = models.ForeignKey(
PolicyBindingModel, on_delete=models.CASCADE, related_name="+"
)
# default value and non-unique for compatibility
order = models.IntegerField(default=0)
class Meta:
verbose_name = _("Policy Binding")
verbose_name_plural = _("Policy Bindings")

View File

@ -83,6 +83,8 @@ INSTALLED_APPS = [
"passbook.admin.apps.PassbookAdminConfig",
"passbook.api.apps.PassbookAPIConfig",
"passbook.lib.apps.PassbookLibConfig",
"passbook.flows.apps.PassbookFlowsConfig",
"passbook.policies.apps.PassbookPoliciesConfig",
"passbook.audit.apps.PassbookAuditConfig",
"passbook.crypto.apps.PassbookCryptoConfig",
"passbook.recovery.apps.PassbookRecoveryConfig",

View File

@ -30,7 +30,11 @@ for _passbook_app in get_apps():
),
)
urlpatterns.append(_path)
LOGGER.debug("Mounted URLs", app_name=_passbook_app.name)
LOGGER.debug(
"Mounted URLs",
app_name=_passbook_app.name,
mountpoint=_passbook_app.mountpoint,
)
urlpatterns += [
# Administration