admin: add flow-stage-bindings, add policy-bindings, add prompts

This commit is contained in:
Jens Langhammer 2020-05-16 19:55:59 +02:00
parent df1cb88abc
commit e68352b09c
17 changed files with 651 additions and 34 deletions

View File

@ -52,23 +52,63 @@
{% trans 'Property Mappings' %} {% trans 'Property Mappings' %}
</a> </a>
</li> </li>
<li class="pf-c-nav__item"> <li class="pf-c-nav__item pf-m-expandable pf-m-expanded pf-m-current">
<a href="{% url 'passbook_admin:flows' %}" <a href="#" class="pf-c-nav__link" aria-expanded="true">{% trans 'Flows' %}
class="pf-c-nav__link {% is_active 'passbook_admin:flows' 'passbook_admin:flow-create' 'passbook_admin:flow-update' 'passbook_admin:flow-delete' %}"> <span class="pf-c-nav__toggle">
{% trans 'Flows' %} <i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
</a> </a>
<section class="pf-c-nav__subnav">
<ul class="pf-c-nav__simple-list">
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:flows' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:flows' 'passbook_admin:flow-create' 'passbook_admin:flow-update' 'passbook_admin:flow-delete' %}">
{% trans 'Flows' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:stages' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:stages' 'passbook_admin:stage-create' 'passbook_admin:stage-update' 'passbook_admin:stage-delete' %}">
{% trans 'Stages' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:stage-prompts' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:stage-prompts' 'passbook_admin:stage-prompt-create' 'passbook_admin:stage-prompt-update' 'passbook_admin:stage-prompt-delete' %}">
{% trans 'Stage Prompts' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:stage-bindings' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:stage-bindings' 'passbook_admin:stage-binding-create' 'passbook_admin:stage-binding-update' 'passbook_admin:stage-binding-delete' %}">
{% trans 'Bindings' %}
</a>
</li>
</ul>
</section>
</li> </li>
<li class="pf-c-nav__item"> <li class="pf-c-nav__item pf-m-expandable pf-m-expanded pf-m-current">
<a href="{% url 'passbook_admin:stages' %}" <a href="#" class="pf-c-nav__link" aria-expanded="true">{% trans 'Policies and Bindings' %}
class="pf-c-nav__link {% is_active 'passbook_admin:stages' 'passbook_admin:stage-create' 'passbook_admin:stage-update' 'passbook_admin:stage-delete' %}"> <span class="pf-c-nav__toggle">
{% trans 'Stages' %} <i class="fas fa-angle-right" aria-hidden="true"></i>
</a> </span>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:policies' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:policies' 'passbook_admin:policy-create' 'passbook_admin:policy-update' 'passbook_admin:policy-delete' 'passbook_admin:policy-test' %}">
{% trans 'Policies' %}
</a> </a>
<section class="pf-c-nav__subnav" aria-labelledby="subnav-title1">
<ul class="pf-c-nav__simple-list">
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:policies' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:policies' 'passbook_admin:policy-create' 'passbook_admin:policy-update' 'passbook_admin:policy-delete' 'passbook_admin:policy-test' %}">
{% trans 'Policies' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:policies-bindings' %}"
class="pf-c-nav__link {% is_active 'passbook_admin:policies-bindings' 'passbook_admin:policy-binding-create' 'passbook_admin:policy-binding-update' 'passbook_admin:policy-binding-delete' %}">
{% trans 'Bindings' %}
</a>
</li>
</ul>
</section>
</li> </li>
<li class="pf-c-nav__item"> <li class="pf-c-nav__item">
<a href="{% url 'passbook_admin:certificate_key_pair' %}" <a href="{% url 'passbook_admin:certificate_key_pair' %}"

View File

@ -0,0 +1,70 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-infrastructure"></i>
{% trans 'Stage Bindings' %}
</h1>
<p>{% trans "Bind existing Stages to Flows." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<a href="{% url 'passbook_admin:stage-binding-create' %}?back={{ request.get_full_path }}"
class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Order' %}</th>
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Stage Type' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for binding in object_list %}
<tr role="row">
<td role="cell">
<span>
{{ binding.order }}
</span>
</td>
<th role="columnheader">
<div>
<div>{{ binding.stage.name }}</div>
<small>
{% blocktrans with flow=binding.flow %}
Bound to {{ flow }}.
{% endblocktrans %}
</small>
</div>
</th>
<td role="cell">
<span>
{{ binding.stage }}
</span>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:stage-binding-update' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:stage-binding-delete' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,66 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-infrastructure"></i>
{% trans 'Policy Bindings' %}
</h1>
<p>{% trans "Bind existing Policies to Models accepting policies." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<a href="{% url 'passbook_admin:policy-binding-create' %}?back={{ request.get_full_path }}"
class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for binding in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ binding.name }}</div>
{% if not binding.bindings.exists %}
<i class="pf-icon pf-icon-warning-triangle"></i>
<small>{% trans 'Warning: Policy is not assigned.' %}</small>
{% else %}
<i class="pf-icon pf-icon-ok"></i>
<small>{% blocktrans with object_count=binding.bindings.all|length %}Assigned to {{ object_count }} objects.{% endblocktrans %}</small>
{% endif %}
</div>
</th>
<td role="cell">
<span>
{{ binding|verbose_name }}
</span>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:policy-binding-update' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:policy-binding-delete' pk=binding.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
</div>
</section>
{% endblock %}

View File

@ -40,7 +40,6 @@
<tr role="row"> <tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th> <th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Flows' %}</th> <th role="columnheader" scope="col">{% trans 'Flows' %}</th>
<th role="columnheader" scope="col">{% trans 'Enabled' %}</th>
<th role="cell"></th> <th role="cell"></th>
</tr> </tr>
</thead> </thead>
@ -60,11 +59,6 @@
{% endfor %} {% endfor %}
</ul> </ul>
</td> </td>
<td role="cell">
<span>
{{ stage.enabled }}
</span>
</td>
<td> <td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:stage-update' pk=stage.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> <a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:stage-update' pk=stage.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:stage-delete' pk=stage.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a> <a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:stage-delete' pk=stage.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>

View File

@ -0,0 +1,66 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load passbook_utils %}
{% load admin_reflection %}
{% block content %}
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-plugged"></i>
{% trans 'Prompts' %}
</h1>
<p>{% trans "Prompts that can be used for Prompt Stages." %}</p>
</div>
</section>
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-top">
<div class="pf-c-toolbar__action-group">
<a href="{% url 'passbook_admin:stage-prompt-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
</div>
{% include 'partials/pagination.html' %}
</div>
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
<thead>
<tr role="row">
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
<th role="columnheader" scope="col">{% trans 'Flows' %}</th>
<th role="cell"></th>
</tr>
</thead>
<tbody role="rowgroup">
{% for prompt in object_list %}
<tr role="row">
<th role="columnheader">
<div>
<div>{{ prompt.field_key }}</div>
<small>{{ prompt|verbose_name }}</small>
</div>
</th>
<td role="cell">
<ul>
{% for flow in prompt.flow_set.all %}
<li><a href="{% url 'passbook_admin:flow-update' pk=flow.pk %}">{{ flow.slug }}</a></li>
{% endfor %}
</ul>
</td>
<td>
<a class="pf-c-button pf-m-secondary" href="{% url 'passbook_admin:prompt-update' pk=prompt.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
<a class="pf-c-button pf-m-danger" href="{% url 'passbook_admin:prompt-delete' pk=prompt.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
{% get_links prompt as links %}
{% for name, href in links.items %}
<a class="pf-c-button pf-m-tertiary" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pf-c-toolbar" id="page-layout-table-simple-toolbar-bottom">
{% include 'partials/pagination.html' %}
</div>
</div>
</section>
{% endblock %}

View File

@ -10,11 +10,14 @@ from passbook.admin.views import (
groups, groups,
invitations, invitations,
overview, overview,
policy, policies,
policies_bindings,
property_mapping, property_mapping,
providers, providers,
sources, sources,
stages, stages,
stages_bindings,
stages_prompts,
users, users,
) )
@ -53,20 +56,43 @@ urlpatterns = [
name="source-delete", name="source-delete",
), ),
# Policies # Policies
path("policies/", policy.PolicyListView.as_view(), name="policies"), path("policies/", policies.PolicyListView.as_view(), name="policies"),
path("policies/create/", policy.PolicyCreateView.as_view(), name="policy-create"), path("policies/create/", policies.PolicyCreateView.as_view(), name="policy-create"),
path( path(
"policies/<uuid:pk>/update/", "policies/<uuid:pk>/update/",
policy.PolicyUpdateView.as_view(), policies.PolicyUpdateView.as_view(),
name="policy-update", name="policy-update",
), ),
path( path(
"policies/<uuid:pk>/delete/", "policies/<uuid:pk>/delete/",
policy.PolicyDeleteView.as_view(), policies.PolicyDeleteView.as_view(),
name="policy-delete", name="policy-delete",
), ),
path( path(
"policies/<uuid:pk>/test/", policy.PolicyTestView.as_view(), name="policy-test" "policies/<uuid:pk>/test/",
policies.PolicyTestView.as_view(),
name="policy-test",
),
# Policy bindings
path(
"policies/bindings/",
policies_bindings.PolicyBindingListView.as_view(),
name="policies-bindings",
),
path(
"policies/bindings/create/",
policies_bindings.PolicyBindingCreateView.as_view(),
name="policy-binding-create",
),
path(
"policies/bindings/<uuid:pk>/update/",
policies_bindings.PolicyBindingUpdateView.as_view(),
name="policy-binding-update",
),
path(
"policies/bindings/<uuid:pk>/delete/",
policies_bindings.PolicyBindingDeleteView.as_view(),
name="policy-binding-delete",
), ),
# Providers # Providers
path("providers/", providers.ProviderListView.as_view(), name="providers"), path("providers/", providers.ProviderListView.as_view(), name="providers"),
@ -98,6 +124,48 @@ urlpatterns = [
stages.StageDeleteView.as_view(), stages.StageDeleteView.as_view(),
name="stage-delete", name="stage-delete",
), ),
# Stage bindings
path(
"stages/bindings/",
stages_bindings.StageBindingListView.as_view(),
name="stage-bindings",
),
path(
"stages/bindings/create/",
stages_bindings.StageBindingCreateView.as_view(),
name="stage-binding-create",
),
path(
"stages/bindings/<uuid:pk>/update/",
stages_bindings.StageBindingUpdateView.as_view(),
name="stage-binding-update",
),
path(
"stages/bindings/<uuid:pk>/delete/",
stages_bindings.StageBindingDeleteView.as_view(),
name="stage-binding-delete",
),
# Stage Prompts
path(
"stages/prompts/",
stages_prompts.PromptListView.as_view(),
name="stage-prompts",
),
path(
"stages/prompts/create/",
stages_prompts.PromptCreateView.as_view(),
name="stage-prompt-create",
),
path(
"stages/prompts/<uuid:pk>/update/",
stages_prompts.PromptUpdateView.as_view(),
name="stage-prompt-update",
),
path(
"stages/prompts/<uuid:pk>/delete/",
stages_prompts.PromptDeleteView.as_view(),
name="stage-prompt-delete",
),
# Flows # Flows
path("flows/", flows.FlowListView.as_view(), name="flows"), path("flows/", flows.FlowListView.as_view(), name="flows"),
path("flows/create/", flows.FlowCreateView.as_view(), name="flow-create",), path("flows/create/", flows.FlowCreateView.as_view(), name="flow-create",),

View File

@ -23,7 +23,7 @@ class PolicyListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all policies""" """Show list of all policies"""
model = Policy model = Policy
permission_required = "passbook_core.view_policy" permission_required = "passbook_policies.view_policy"
paginate_by = 10 paginate_by = 10
ordering = "order" ordering = "order"
template_name = "administration/policy/list.html" template_name = "administration/policy/list.html"
@ -47,7 +47,7 @@ class PolicyCreateView(
"""Create new Policy""" """Create new Policy"""
model = Policy model = Policy
permission_required = "passbook_core.add_policy" permission_required = "passbook_policies.add_policy"
template_name = "generic/create.html" template_name = "generic/create.html"
success_url = reverse_lazy("passbook_admin:policies") success_url = reverse_lazy("passbook_admin:policies")
@ -74,7 +74,7 @@ class PolicyUpdateView(
"""Update policy""" """Update policy"""
model = Policy model = Policy
permission_required = "passbook_core.change_policy" permission_required = "passbook_policies.change_policy"
template_name = "generic/update.html" template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:policies") success_url = reverse_lazy("passbook_admin:policies")
@ -104,7 +104,7 @@ class PolicyDeleteView(
"""Delete policy""" """Delete policy"""
model = Policy model = Policy
permission_required = "passbook_core.delete_policy" permission_required = "passbook_policies.delete_policy"
template_name = "generic/delete.html" template_name = "generic/delete.html"
success_url = reverse_lazy("passbook_admin:policies") success_url = reverse_lazy("passbook_admin:policies")
@ -125,7 +125,7 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
model = Policy model = Policy
form_class = PolicyTestForm form_class = PolicyTestForm
permission_required = "passbook_core.view_policy" permission_required = "passbook_policies.view_policy"
template_name = "administration/policy/test.html" template_name = "administration/policy/test.html"
object = None object = None

View File

@ -0,0 +1,100 @@
"""passbook PolicyBinding 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.lib.utils.reflection import path_to_class
from passbook.lib.views import CreateAssignPermView
from passbook.policies.forms import PolicyBindingForm
from passbook.policies.models import PolicyBinding
class PolicyBindingListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all policies"""
model = PolicyBinding
permission_required = "passbook_policies.view_policybinding"
paginate_by = 10
ordering = "order"
template_name = "administration/policybinding/list.html"
class PolicyBindingCreateView(
SuccessMessageMixin,
LoginRequiredMixin,
DjangoPermissionRequiredMixin,
CreateAssignPermView,
):
"""Create new PolicyBinding"""
model = PolicyBinding
permission_required = "passbook_policies.add_policybinding"
form_class = PolicyBindingForm
template_name = "generic/create.html"
success_url = reverse_lazy("passbook_admin:policies")
success_message = _("Successfully created PolicyBinding")
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
form_cls = self.get_form_class()
if hasattr(form_cls, "template_name"):
kwargs["base_template"] = form_cls.template_name
return kwargs
class PolicyBindingUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
"""Update policybinding"""
model = PolicyBinding
permission_required = "passbook_policies.change_policybinding"
form_class = PolicyBindingForm
template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:policies")
success_message = _("Successfully updated PolicyBinding")
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
form_cls = self.get_form_class()
if hasattr(form_cls, "template_name"):
kwargs["base_template"] = form_cls.template_name
return kwargs
def get_form_class(self):
form_class_path = self.get_object().form
form_class = path_to_class(form_class_path)
return form_class
def get_object(self, queryset=None):
return (
PolicyBinding.objects.filter(pk=self.kwargs.get("pk"))
.select_subclasses()
.first()
)
class PolicyBindingDeleteView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
):
"""Delete policybinding"""
model = PolicyBinding
permission_required = "passbook_policies.delete_policybinding"
template_name = "generic/delete.html"
success_url = reverse_lazy("passbook_admin:policies")
success_message = _("Successfully deleted PolicyBinding")
def delete(self, request, *args, **kwargs):
messages.success(self.request, self.success_message)
return super().delete(request, *args, **kwargs)

View File

@ -0,0 +1,87 @@
"""passbook StageBinding 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 FlowStageBindingForm
from passbook.flows.models import FlowStageBinding
from passbook.lib.views import CreateAssignPermView
class StageBindingListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all flows"""
model = FlowStageBinding
permission_required = "passbook_flows.view_flowstagebinding"
paginate_by = 10
ordering = "order"
template_name = "administration/flowstagebinding/list.html"
class StageBindingCreateView(
SuccessMessageMixin,
LoginRequiredMixin,
DjangoPermissionRequiredMixin,
CreateAssignPermView,
):
"""Create new StageBinding"""
model = FlowStageBinding
permission_required = "passbook_flows.add_flowstagebinding"
form_class = FlowStageBindingForm
template_name = "generic/create.html"
success_url = reverse_lazy("passbook_admin:flows")
success_message = _("Successfully created StageBinding")
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
form_cls = self.get_form_class()
if hasattr(form_cls, "template_name"):
kwargs["base_template"] = form_cls.template_name
return kwargs
class StageBindingUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
"""Update FlowStageBinding"""
model = FlowStageBinding
permission_required = "passbook_flows.change_flowstagebinding"
form_class = FlowStageBindingForm
template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:flows")
success_message = _("Successfully updated StageBinding")
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
form_cls = self.get_form_class()
if hasattr(form_cls, "template_name"):
kwargs["base_template"] = form_cls.template_name
return kwargs
class StageBindingDeleteView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
):
"""Delete FlowStageBinding"""
model = FlowStageBinding
permission_required = "passbook_flows.delete_flowstagebinding"
template_name = "generic/delete.html"
success_url = reverse_lazy("passbook_admin:flows")
success_message = _("Successfully deleted FlowStageBinding")
def delete(self, request, *args, **kwargs):
messages.success(self.request, self.success_message)
return super().delete(request, *args, **kwargs)

View File

@ -0,0 +1,77 @@
"""passbook Prompt 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.lib.views import CreateAssignPermView
from passbook.stages.prompt.forms import PromptAdminForm
from passbook.stages.prompt.models import Prompt
class PromptListView(LoginRequiredMixin, PermissionListMixin, ListView):
"""Show list of all prompts"""
model = Prompt
permission_required = "passbook_stages_prompt.view_prompt"
ordering = "field_key"
paginate_by = 40
template_name = "administration/stage_prompt/list.html"
class PromptCreateView(
SuccessMessageMixin,
LoginRequiredMixin,
DjangoPermissionRequiredMixin,
CreateAssignPermView,
):
"""Create new Prompt"""
model = Prompt
form_class = PromptAdminForm
permission_required = "passbook_stages_prompt.add_prompt"
template_name = "generic/create.html"
success_url = reverse_lazy("passbook_admin:prompts")
success_message = _("Successfully created Prompt")
def get_context_data(self, **kwargs):
kwargs["type"] = "Prompt"
return super().get_context_data(**kwargs)
class PromptUpdateView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView
):
"""Update prompt"""
model = Prompt
form_class = PromptAdminForm
permission_required = "passbook_stages_prompt.change_prompt"
template_name = "generic/update.html"
success_url = reverse_lazy("passbook_admin:prompts")
success_message = _("Successfully updated Prompt")
class PromptDeleteView(
SuccessMessageMixin, LoginRequiredMixin, PermissionRequiredMixin, DeleteView
):
"""Delete prompt"""
model = Prompt
permission_required = "passbook_stages_prompt.delete_prompt"
template_name = "generic/delete.html"
success_url = reverse_lazy("passbook_admin:prompts")
success_message = _("Successfully deleted Prompt")
def delete(self, request, *args, **kwargs):
messages.success(self.request, self.success_message)
return super().delete(request, *args, **kwargs)

View File

@ -11,7 +11,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
# ("passbook_policies", "0001_initial"), ("passbook_policies", "0001_initial"),
] ]
operations = [ operations = [

View File

@ -76,7 +76,7 @@ def create_default_invalidation_flow(
return return
if not UserLogoutStage.objects.using(db_alias).exists(): if not UserLogoutStage.objects.using(db_alias).exists():
UserLogoutStage.objects.using(db_alias).create(name="authentication") UserLogoutStage.objects.using(db_alias).create(name="logout")
flow = Flow.objects.using(db_alias).create( flow = Flow.objects.using(db_alias).create(
name="default-invalidation-flow", name="default-invalidation-flow",

View File

@ -89,6 +89,8 @@ class FlowStageBinding(PolicyBindingModel, UUIDModel):
order = models.IntegerField() order = models.IntegerField()
objects = InheritanceManager()
def __str__(self) -> str: def __str__(self) -> str:
return f"Flow Stage Binding #{self.order} {self.flow} -> {self.stage}" return f"Flow Stage Binding #{self.order} {self.flow} -> {self.stage}"

View File

@ -1,4 +1,25 @@
"""General fields""" """General fields"""
from django import forms
from passbook.policies.models import PolicyBinding, PolicyBindingModel
GENERAL_FIELDS = ["name", "negate", "order", "timeout"] GENERAL_FIELDS = ["name", "negate", "order", "timeout"]
GENERAL_SERIALIZER_FIELDS = ["pk", "name", "negate", "order", "timeout"] GENERAL_SERIALIZER_FIELDS = ["pk", "name", "negate", "order", "timeout"]
class PolicyBindingForm(forms.ModelForm):
"""Form to edit Policy to PolicyBindingModel Binding"""
target = forms.ModelChoiceField(
queryset=PolicyBindingModel.objects.all().select_subclasses()
)
class Meta:
model = PolicyBinding
fields = [
"enabled",
"policy",
"target",
"order",
]

View File

@ -15,6 +15,8 @@ class PolicyBindingModel(models.Model):
"Policy", through="PolicyBinding", related_name="bindings", blank=True "Policy", through="PolicyBinding", related_name="bindings", blank=True
) )
objects = InheritanceManager()
class Meta: class Meta:
verbose_name = _("Policy Binding Model") verbose_name = _("Policy Binding Model")

View File

@ -19,6 +19,25 @@ class PromptStageForm(forms.ModelForm):
} }
class PromptAdminForm(forms.ModelForm):
"""Form to edit Prompt instances for admins"""
class Meta:
model = Prompt
fields = [
"field_key",
"label",
"type",
"required",
"placeholder",
]
widgets = {
"label": forms.TextInput(),
"placeholder": forms.TextInput(),
}
class PromptForm(forms.Form): class PromptForm(forms.Form):
"""Dynamically created form based on PromptStage""" """Dynamically created form based on PromptStage"""

View File

@ -29,12 +29,17 @@ class UserWriteStageView(AuthenticationStage):
user = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] user = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
for key, value in data.items(): for key, value in data.items():
setter_name = f"set_{key}" setter_name = f"set_{key}"
# Check if user has a setter for this key, like set_password
if hasattr(user, setter_name): if hasattr(user, setter_name):
setter = getattr(user, setter_name) setter = getattr(user, setter_name)
if callable(setter): if callable(setter):
setter(value) setter(value)
else: # User has this key already
elif hasattr(user, key):
setattr(user, key, value) setattr(user, key, value)
# Otherwise we just save it as custom attribute
else:
user.attributes[key] = value
user.save() user.save()
LOGGER.debug( LOGGER.debug(
"Updated existing user", user=user, flow_slug=self.executor.flow.slug, "Updated existing user", user=user, flow_slug=self.executor.flow.slug,