From 08c0eb2ec6d690dec1a9c27d1547e098feb86931 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 8 May 2020 18:45:53 +0200 Subject: [PATCH] admin: add flows --- manage.py | 4 +- .../admin/templates/administration/base.html | 6 ++ .../templates/administration/flow/list.html | 71 +++++++++++++++++ passbook/admin/urls.py | 10 +++ passbook/admin/views/flows.py | 77 +++++++++++++++++++ passbook/flows/api.py | 18 +---- .../migrations/0005_auto_20200508_1642.py | 23 ++++++ passbook/flows/models.py | 2 +- .../migrations/0003_auto_20200508_1642.py | 24 ++++++ passbook/policies/models.py | 4 +- 10 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 passbook/admin/templates/administration/flow/list.html create mode 100644 passbook/admin/views/flows.py create mode 100644 passbook/flows/migrations/0005_auto_20200508_1642.py create mode 100644 passbook/policies/migrations/0003_auto_20200508_1642.py diff --git a/manage.py b/manage.py index a9cc30054..5a70f4ada 100755 --- a/manage.py +++ b/manage.py @@ -6,8 +6,8 @@ from defusedxml import defuse_stdlib defuse_stdlib() -if __name__ == '__main__': - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'passbook.root.settings') +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passbook.root.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/passbook/admin/templates/administration/base.html b/passbook/admin/templates/administration/base.html index db483f1b9..51240c85e 100644 --- a/passbook/admin/templates/administration/base.html +++ b/passbook/admin/templates/administration/base.html @@ -52,6 +52,12 @@ {% trans 'Property Mappings' %} +
  • + + {% trans 'Flows' %} + +
  • diff --git a/passbook/admin/templates/administration/flow/list.html b/passbook/admin/templates/administration/flow/list.html new file mode 100644 index 000000000..c22b38dea --- /dev/null +++ b/passbook/admin/templates/administration/flow/list.html @@ -0,0 +1,71 @@ +{% extends "administration/base.html" %} + +{% load i18n %} +{% load utils %} + +{% block content %} +
    +
    +

    + + {% trans 'Flows' %} +

    +

    {% trans "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them." %}

    +
    +
    +
    +
    +
    + + {% include 'partials/pagination.html' %} +
    + + + + + + + + + + + + {% for flow in object_list %} + + + + + + + + {% endfor %} + +
    {% trans 'Name' %}{% trans 'Designation' %}{% trans 'Factors' %}{% trans 'Policies' %}
    +
    +
    {{ flow.name }}
    + {{ flow.slug }} +
    +
    + + {{ flow.designation }} + + + + {{ flow.factors.all|length }} + + + + {{ flow.policies.all|length }} + + + {% trans 'Edit' %} + {% trans 'Delete' %} +
    +
    + {% include 'partials/pagination.html' %} +
    +
    +
    +{% endblock %} diff --git a/passbook/admin/urls.py b/passbook/admin/urls.py index 7806471d7..6435a446d 100644 --- a/passbook/admin/urls.py +++ b/passbook/admin/urls.py @@ -7,6 +7,7 @@ from passbook.admin.views import ( certificate_key_pair, debug, factors, + flows, groups, invitations, overview, @@ -97,6 +98,15 @@ urlpatterns = [ factors.FactorDeleteView.as_view(), name="factor-delete", ), + # Flows + path("flows/", flows.FlowListView.as_view(), name="flows"), + path("flows/create/", flows.FlowCreateView.as_view(), name="flow-create",), + path( + "flows//update/", flows.FlowUpdateView.as_view(), name="flow-update", + ), + path( + "flows//delete/", flows.FlowDeleteView.as_view(), name="flow-delete", + ), # Factors path( "property-mappings/", diff --git a/passbook/admin/views/flows.py b/passbook/admin/views/flows.py new file mode 100644 index 000000000..377cf93f4 --- /dev/null +++ b/passbook/admin/views/flows.py @@ -0,0 +1,77 @@ +"""passbook Flow administration""" +from django.contrib import messages +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth.mixins import ( + PermissionRequiredMixin as DjangoPermissionRequiredMixin, +) +from django.contrib.messages.views import SuccessMessageMixin +from django.urls import reverse_lazy +from django.utils.translation import ugettext as _ +from django.views.generic import DeleteView, ListView, UpdateView +from guardian.mixins import PermissionListMixin, PermissionRequiredMixin + +from passbook.flows.forms import FlowForm +from passbook.flows.models import Flow +from passbook.lib.views import CreateAssignPermView + + +class FlowListView(LoginRequiredMixin, PermissionListMixin, ListView): + """Show list of all flows""" + + model = Flow + permission_required = "passbook_flows.view_flow" + ordering = "name" + paginate_by = 40 + template_name = "administration/flow/list.html" + + +class FlowCreateView( + SuccessMessageMixin, + LoginRequiredMixin, + DjangoPermissionRequiredMixin, + CreateAssignPermView, +): + """Create new Flow""" + + model = Flow + form_class = FlowForm + permission_required = "passbook_flows.add_flow" + + template_name = "generic/create.html" + success_url = reverse_lazy("passbook_admin:flows") + success_message = _("Successfully created Flow") + + def get_context_data(self, **kwargs): + kwargs["type"] = "Flow" + return super().get_context_data(**kwargs) + + +class FlowUpdateView( + SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView +): + """Update flow""" + + model = Flow + form_class = FlowForm + permission_required = "passbook_flows.change_flow" + + template_name = "generic/update.html" + success_url = reverse_lazy("passbook_admin:flows") + success_message = _("Successfully updated Flow") + + +class FlowDeleteView( + SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView +): + """Delete flow""" + + model = Flow + permission_required = "passbook_flows.delete_flow" + + template_name = "generic/delete.html" + success_url = reverse_lazy("passbook_admin:flows") + success_message = _("Successfully deleted Flow") + + def delete(self, request, *args, **kwargs): + messages.success(self.request, self.success_message) + return super().delete(request, *args, **kwargs) diff --git a/passbook/flows/api.py b/passbook/flows/api.py index 09599ca6e..9edc3d9e5 100644 --- a/passbook/flows/api.py +++ b/passbook/flows/api.py @@ -11,14 +11,7 @@ class FlowSerializer(ModelSerializer): class Meta: model = Flow - fields = [ - "pk", - "name", - "slug", - "designation", - "factors", - "policies" - ] + fields = ["pk", "name", "slug", "designation", "factors", "policies"] class FlowViewSet(ModelViewSet): @@ -34,14 +27,7 @@ class FlowFactorBindingSerializer(ModelSerializer): class Meta: model = FlowFactorBinding - fields = [ - "pk", - "flow", - "factor", - "re_evaluate_policies", - "order", - "policies" - ] + fields = ["pk", "flow", "factor", "re_evaluate_policies", "order", "policies"] class FlowFactorBindingViewSet(ModelViewSet): diff --git a/passbook/flows/migrations/0005_auto_20200508_1642.py b/passbook/flows/migrations/0005_auto_20200508_1642.py new file mode 100644 index 000000000..3bf11a86e --- /dev/null +++ b/passbook/flows/migrations/0005_auto_20200508_1642.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.3 on 2020-05-08 16:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_core", "0011_auto_20200222_1822"), + ("passbook_flows", "0004_default_flows"), + ] + + operations = [ + migrations.AlterField( + model_name="flow", + name="factors", + field=models.ManyToManyField( + blank=True, + through="passbook_flows.FlowFactorBinding", + to="passbook_core.Factor", + ), + ), + ] diff --git a/passbook/flows/models.py b/passbook/flows/models.py index 44cda4f35..5169fc1d8 100644 --- a/passbook/flows/models.py +++ b/passbook/flows/models.py @@ -36,7 +36,7 @@ class Flow(PolicyBindingModel, UUIDModel): designation = models.CharField(max_length=100, choices=FlowDesignation.as_choices()) - factors = models.ManyToManyField(Factor, through="FlowFactorBinding") + factors = models.ManyToManyField(Factor, through="FlowFactorBinding", blank=True) pbm = models.OneToOneField( PolicyBindingModel, parent_link=True, on_delete=models.CASCADE, related_name="+" diff --git a/passbook/policies/migrations/0003_auto_20200508_1642.py b/passbook/policies/migrations/0003_auto_20200508_1642.py new file mode 100644 index 000000000..a9e6128ba --- /dev/null +++ b/passbook/policies/migrations/0003_auto_20200508_1642.py @@ -0,0 +1,24 @@ +# Generated by Django 3.0.3 on 2020-05-08 16:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_core", "0011_auto_20200222_1822"), + ("passbook_policies", "0002_auto_20200508_1230"), + ] + + operations = [ + migrations.AlterField( + model_name="policybindingmodel", + name="policies", + field=models.ManyToManyField( + blank=True, + related_name="_policybindingmodel_policies_+", + through="passbook_policies.PolicyBinding", + to="passbook_core.Policy", + ), + ), + ] diff --git a/passbook/policies/models.py b/passbook/policies/models.py index 42f85f74c..033c818a0 100644 --- a/passbook/policies/models.py +++ b/passbook/policies/models.py @@ -9,7 +9,9 @@ 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="+") + policies = models.ManyToManyField( + Policy, through="PolicyBinding", related_name="+", blank=True + ) class Meta: