flows: move complete denied view and template to flows
This commit is contained in:
parent
3cf558d594
commit
40614a65fc
|
@ -22,6 +22,7 @@ from passbook.lib.models import CreatedUpdatedModel
|
||||||
from passbook.policies.models import PolicyBindingModel
|
from passbook.policies.models import PolicyBindingModel
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
PASSBOOK_USER_DEBUG = "passbook_user_debug"
|
||||||
|
|
||||||
|
|
||||||
def default_token_duration():
|
def default_token_duration():
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
{# Template used by bad_request_message within flows #}
|
|
||||||
{% extends 'login/base.html' %}
|
|
||||||
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load passbook_utils %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans card_title %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block card_title %}
|
|
||||||
{% trans card_title %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block card %}
|
|
||||||
<form method="POST" class="pf-c-form">
|
|
||||||
{% if message %}
|
|
||||||
<h3>{% trans message %}</h3>
|
|
||||||
{% endif %}
|
|
||||||
{% if 'back' in request.GET %}
|
|
||||||
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,29 +0,0 @@
|
||||||
{% extends 'login/base_full.html' %}
|
|
||||||
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load passbook_utils %}
|
|
||||||
|
|
||||||
{% block card_title %}
|
|
||||||
{% trans 'Permission denied' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans 'Permission denied' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block card %}
|
|
||||||
<form method="POST" class="pf-c-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% include 'partials/form.html' %}
|
|
||||||
<div class="pf-c-form__group">
|
|
||||||
<p>
|
|
||||||
<i class="pf-icon pf-icon-error-circle-o"></i>
|
|
||||||
{% trans 'Access denied' %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{% if 'back' in request.GET %}
|
|
||||||
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
|
@ -1,30 +0,0 @@
|
||||||
"""passbook util view tests"""
|
|
||||||
import string
|
|
||||||
from random import SystemRandom
|
|
||||||
|
|
||||||
from django.test import RequestFactory, TestCase
|
|
||||||
|
|
||||||
from passbook.core.models import User
|
|
||||||
from passbook.core.views.utils import PermissionDeniedView
|
|
||||||
|
|
||||||
|
|
||||||
class TestUtilViews(TestCase):
|
|
||||||
"""Test Utility Views"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.user = User.objects.create_superuser(
|
|
||||||
username="unittest user",
|
|
||||||
email="unittest@example.com",
|
|
||||||
password="".join(
|
|
||||||
SystemRandom().choice(string.ascii_uppercase + string.digits)
|
|
||||||
for _ in range(8)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
self.factory = RequestFactory()
|
|
||||||
|
|
||||||
def test_permission_denied_view(self):
|
|
||||||
"""Test PermissionDeniedView"""
|
|
||||||
request = self.factory.get("something")
|
|
||||||
request.user = self.user
|
|
||||||
response = PermissionDeniedView.as_view()(request)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
|
@ -1,14 +0,0 @@
|
||||||
"""passbook core utils view"""
|
|
||||||
from django.utils.translation import gettext as _
|
|
||||||
from django.views.generic import TemplateView
|
|
||||||
|
|
||||||
|
|
||||||
class PermissionDeniedView(TemplateView):
|
|
||||||
"""Generic Permission denied view"""
|
|
||||||
|
|
||||||
template_name = "login/denied.html"
|
|
||||||
title = _("Permission denied.")
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
kwargs["title"] = self.title
|
|
||||||
return super().get_context_data(**kwargs)
|
|
57
passbook/flows/templates/flows/denied.html
Normal file
57
passbook/flows/templates/flows/denied.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{% extends 'login/base_full.html' %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load passbook_utils %}
|
||||||
|
|
||||||
|
{% block card_title %}
|
||||||
|
{% trans 'Permission denied' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% trans 'Permission denied' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block card %}
|
||||||
|
<form method="POST" class="pf-c-form">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% include 'partials/form.html' %}
|
||||||
|
<div class="pf-c-form__group">
|
||||||
|
<p>
|
||||||
|
<i class="pf-icon pf-icon-error-circle-o"></i>
|
||||||
|
{% trans 'Access denied' %}
|
||||||
|
</p>
|
||||||
|
{% if error %}
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
{{ error }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if policy_result %}
|
||||||
|
<hr>
|
||||||
|
<em>
|
||||||
|
{% trans 'Explanation:' %}
|
||||||
|
</em>
|
||||||
|
<ul class="pf-c-list">
|
||||||
|
{% for source_result in policy_result.source_results %}
|
||||||
|
<li>
|
||||||
|
{% blocktrans with name=source_result.source_policy.name result=source_result.passing %}
|
||||||
|
Policy '{{ name }}' returned result '{{ result }}'
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% if source_result.messages %}
|
||||||
|
<ul class="pf-c-list">
|
||||||
|
{% for message in source_result.messages %}
|
||||||
|
<li>{{ message }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if 'back' in request.GET %}
|
||||||
|
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -12,18 +12,18 @@ from django.http import (
|
||||||
from django.shortcuts import get_object_or_404, redirect, render, reverse
|
from django.shortcuts import get_object_or_404, redirect, render, reverse
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django.views.decorators.clickjacking import xframe_options_sameorigin
|
from django.views.decorators.clickjacking import xframe_options_sameorigin
|
||||||
from django.views.generic import TemplateView, View
|
from django.views.generic import TemplateView, View
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
|
|
||||||
from passbook.audit.models import cleanse_dict
|
from passbook.audit.models import cleanse_dict
|
||||||
from passbook.core.views.utils import PermissionDeniedView
|
from passbook.core.models import PASSBOOK_USER_DEBUG
|
||||||
from passbook.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
from passbook.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||||
from passbook.flows.models import Flow, FlowDesignation, Stage
|
from passbook.flows.models import Flow, FlowDesignation, Stage
|
||||||
from passbook.flows.planner import FlowPlan, FlowPlanner
|
from passbook.flows.planner import FlowPlan, FlowPlanner
|
||||||
from passbook.lib.utils.reflection import class_to_path
|
from passbook.lib.utils.reflection import class_to_path
|
||||||
from passbook.lib.utils.urls import is_url_absolute, redirect_with_qs
|
from passbook.lib.utils.urls import is_url_absolute, redirect_with_qs
|
||||||
from passbook.lib.views import bad_request_message
|
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
# Argument used to redirect user after login
|
# Argument used to redirect user after login
|
||||||
|
@ -31,6 +31,8 @@ NEXT_ARG_NAME = "next"
|
||||||
SESSION_KEY_PLAN = "passbook_flows_plan"
|
SESSION_KEY_PLAN = "passbook_flows_plan"
|
||||||
SESSION_KEY_APPLICATION_PRE = "passbook_flows_application_pre"
|
SESSION_KEY_APPLICATION_PRE = "passbook_flows_application_pre"
|
||||||
SESSION_KEY_GET = "passbook_flows_get"
|
SESSION_KEY_GET = "passbook_flows_get"
|
||||||
|
SESSION_KEY_DENIED_ERROR = "passbook_flows_denied_error"
|
||||||
|
SESSION_KEY_DENIED_POLICY_RESULT = "passbook_flows_denied_policy_result"
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(xframe_options_sameorigin, name="dispatch")
|
@method_decorator(xframe_options_sameorigin, name="dispatch")
|
||||||
|
@ -54,9 +56,9 @@ class FlowExecutorView(View):
|
||||||
LOGGER.debug("f(exec): Redirecting to next on fail")
|
LOGGER.debug("f(exec): Redirecting to next on fail")
|
||||||
return redirect(self.request.GET.get(NEXT_ARG_NAME))
|
return redirect(self.request.GET.get(NEXT_ARG_NAME))
|
||||||
message = exc.__doc__ if exc.__doc__ else str(exc)
|
message = exc.__doc__ if exc.__doc__ else str(exc)
|
||||||
|
self.cancel()
|
||||||
return to_stage_response(
|
return to_stage_response(
|
||||||
self.request,
|
self.request, self.stage_invalid(error_message=message)
|
||||||
bad_request_message(self.request, message, template="error/embedded.html"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def dispatch(self, request: HttpRequest, flow_slug: str) -> HttpResponse:
|
def dispatch(self, request: HttpRequest, flow_slug: str) -> HttpResponse:
|
||||||
|
@ -196,11 +198,19 @@ class FlowExecutorView(View):
|
||||||
)
|
)
|
||||||
return self._flow_done()
|
return self._flow_done()
|
||||||
|
|
||||||
def stage_invalid(self) -> HttpResponse:
|
def stage_invalid(self, error_message: Optional[str] = None) -> HttpResponse:
|
||||||
"""Callback used stage when data is correct but a policy denies access
|
"""Callback used stage when data is correct but a policy denies access
|
||||||
or the user account is disabled."""
|
or the user account is disabled.
|
||||||
|
|
||||||
|
Optionally, an exception can be passed, which will be shown if the current user
|
||||||
|
is a superuser."""
|
||||||
LOGGER.debug("f(exec): Stage invalid", flow_slug=self.flow.slug)
|
LOGGER.debug("f(exec): Stage invalid", flow_slug=self.flow.slug)
|
||||||
self.cancel()
|
self.cancel()
|
||||||
|
if self.request.user and self.request.user.is_authenticated:
|
||||||
|
if self.request.user.is_superuser or self.request.user.attributes.get(
|
||||||
|
PASSBOOK_USER_DEBUG, False
|
||||||
|
):
|
||||||
|
self.request.session[SESSION_KEY_DENIED_ERROR] = error_message
|
||||||
return redirect_with_qs("passbook_flows:denied", self.request.GET)
|
return redirect_with_qs("passbook_flows:denied", self.request.GET)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
|
@ -215,9 +225,22 @@ class FlowExecutorView(View):
|
||||||
del self.request.session[key]
|
del self.request.session[key]
|
||||||
|
|
||||||
|
|
||||||
class FlowPermissionDeniedView(PermissionDeniedView):
|
class FlowPermissionDeniedView(TemplateView):
|
||||||
"""User could not be authenticated"""
|
"""User could not be authenticated"""
|
||||||
|
|
||||||
|
template_name = "flows/denied.html"
|
||||||
|
title = _("Permission denied.")
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
kwargs["title"] = self.title
|
||||||
|
if SESSION_KEY_DENIED_ERROR in self.request.session:
|
||||||
|
kwargs["error"] = self.request.session[SESSION_KEY_DENIED_ERROR]
|
||||||
|
if SESSION_KEY_DENIED_POLICY_RESULT in self.request.session:
|
||||||
|
kwargs["policy_result"] = self.request.session[
|
||||||
|
SESSION_KEY_DENIED_POLICY_RESULT
|
||||||
|
]
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class FlowExecutorShellView(TemplateView):
|
class FlowExecutorShellView(TemplateView):
|
||||||
"""Executor Shell view, loads a dummy card with a spinner
|
"""Executor Shell view, loads a dummy card with a spinner
|
||||||
|
|
Reference in a new issue