flows: cleanup denied view, use everywhere

This commit is contained in:
Jens Langhammer 2020-07-02 13:48:42 +02:00
parent 76e2ba4764
commit bead19c64c
9 changed files with 79 additions and 32 deletions

View file

@ -22,8 +22,7 @@
<div class="pf-c-login__container"> <div class="pf-c-login__container">
<header class="pf-c-login__header"> <header class="pf-c-login__header">
<img class="pf-c-brand" src="{% static 'passbook/logo.svg' %}" style="height: 60px;" alt="passbook icon" /> <img class="pf-c-brand" src="{% static 'passbook/logo.svg' %}" style="height: 60px;" alt="passbook icon" />
<img class="pf-c-brand" src="{% static 'passbook/brand.svg' %}" style="height: 60px;" <img class="pf-c-brand" src="{% static 'passbook/brand.svg' %}" style="height: 60px;" alt="passbook branding" />
alt="passbook branding" />
</header> </header>
<main class="pf-c-login__main" id="flow-body"> <main class="pf-c-login__main" id="flow-body">
<header class="pf-c-login__main-header"> <header class="pf-c-login__main-header">
@ -50,7 +49,7 @@
<li> <li>
<a href="https://passbook.beryju.org/">{% trans 'Documentation' %}</a> <a href="https://passbook.beryju.org/">{% trans 'Documentation' %}</a>
</li> </li>
<!-- todo: load config.passbook.footer.links --> <!-- TODO:load config.passbook.footer.links -->
</ul> </ul>
</footer> </footer>
</div> </div>

View file

@ -1,11 +1,38 @@
{% extends 'login/base.html' %} {% extends 'base/skeleton.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% load passbook_utils %} {% load passbook_utils %}
{% block card %} {% block body %}
<form method="POST" class="pf-c-form"> <div class="pf-c-background-image">
<svg xmlns="http://www.w3.org/2000/svg" class="pf-c-background-image__filter" width="0" height="0">
<filter id="image_overlay">
<feColorMatrix type="matrix" values="1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0"></feColorMatrix>
<feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
<feFuncR type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncR>
<feFuncG type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncG>
<feFuncB type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncB>
<feFuncA type="table" tableValues="0 1"></feFuncA>
</feComponentTransfer>
</filter>
</svg>
</div>
<div class="pf-c-login">
<div class="pf-c-login__container">
<header class="pf-c-login__header">
<img class="pf-c-brand" src="{% static 'passbook/logo.svg' %}" style="height: 60px;" alt="passbook icon" />
<img class="pf-c-brand" src="{% static 'passbook/brand.svg' %}" style="height: 60px;" alt="passbook branding" />
</header>
<main class="pf-c-login__main" id="flow-body">
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
{% trans 'Permission denied' %}
</h1>
</header>
<div class="pf-c-login__main-body">
{% block card %}
<form method="POST" class="pf-c-form">
{% csrf_token %} {% csrf_token %}
{% include 'partials/form.html' %} {% include 'partials/form.html' %}
<div class="pf-c-form__group"> <div class="pf-c-form__group">
@ -17,5 +44,19 @@
{% if 'back' in request.GET %} {% if 'back' in request.GET %}
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a> <a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
{% endif %} {% endif %}
</form> </form>
{% endblock %}
</div>
</main>
<footer class="pf-c-login__footer">
<p></p>
<ul class="pf-c-list pf-m-inline">
<li>
<a href="https://passbook.beryju.org/">{% trans 'Documentation' %}</a>
</li>
<!-- TODO: load config.passbook.footer.links -->
</ul>
</footer>
</div>
</div>
{% endblock %} {% endblock %}

View file

@ -50,7 +50,7 @@
<div class="pf-c-empty-state__body"> <div class="pf-c-empty-state__body">
{% trans "Either no applications are defined, or you don't have access to any." %} {% trans "Either no applications are defined, or you don't have access to any." %}
</div> </div>
{% if user.is_superuser %} {# todo: use guardian permissions instead #} {% if user.is_superuser %} {# TODO:use guardian permissions instead #}
<a href="{% url 'passbook_admin:application-create' %}" class="pf-c-button pf-m-primary" type="button"> <a href="{% url 'passbook_admin:application-create' %}" class="pf-c-button pf-m-primary" type="button">
{% trans 'Create Application' %} {% trans 'Create Application' %}
</a> </a>

View file

@ -59,7 +59,7 @@
<li> <li>
<a href="https://passbook.beryju.org/">{% trans 'Documentation' %}</a> <a href="https://passbook.beryju.org/">{% trans 'Documentation' %}</a>
</li> </li>
<!-- todo: load config.passbook.footer.links --> <!-- TODO:load config.passbook.footer.links -->
</ul> </ul>
</footer> </footer>
</div> </div>

View file

@ -2,7 +2,9 @@
from typing import Optional from typing import Optional
from django.contrib import messages from django.contrib import messages
from django.http import HttpRequest from django.contrib.auth.mixins import AccessMixin
from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from structlog import get_logger from structlog import get_logger
@ -19,10 +21,14 @@ class BaseMixin:
request: HttpRequest request: HttpRequest
class PolicyAccessMixin(BaseMixin): class PolicyAccessMixin(BaseMixin, AccessMixin):
"""Mixin class for usage in Authorization views. """Mixin class for usage in Authorization views.
Provider functions to check application access, etc""" Provider functions to check application access, etc"""
def handle_no_permission_authorized(self) -> HttpResponse:
"""Function called when user has no permissions but is authorized"""
return redirect("passbook_flows:denied")
def provider_to_application(self, provider: Provider) -> Application: def provider_to_application(self, provider: Provider) -> Application:
"""Lookup application assigned to provider, throw error if no application assigned""" """Lookup application assigned to provider, throw error if no application assigned"""
try: try:

View file

@ -1,7 +1,7 @@
"""passbook OAuth2 Views""" """passbook OAuth2 Views"""
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404
from django.views import View from django.views import View
from oauth2_provider.exceptions import OAuthToolkitError from oauth2_provider.exceptions import OAuthToolkitError
from oauth2_provider.scopes import get_scopes_backend from oauth2_provider.scopes import get_scopes_backend
@ -48,11 +48,11 @@ class AuthorizationFlowInitView(PolicyAccessMixin, LoginRequiredMixin, View):
try: try:
application = self.provider_to_application(provider) application = self.provider_to_application(provider)
except Application.DoesNotExist: except Application.DoesNotExist:
return redirect("passbook_providers_oauth:oauth2-permission-denied") return self.handle_no_permission_authorized()
# Check permissions # Check permissions
result = self.user_has_access(application) result = self.user_has_access(application)
if not result.passing: if not result.passing:
return redirect("passbook_providers_oauth:oauth2-permission-denied") return self.handle_no_permission_authorized()
# Regardless, we start the planner and return to it # Regardless, we start the planner and return to it
planner = FlowPlanner(provider.authorization_flow) planner = FlowPlanner(provider.authorization_flow)
planner.allow_empty_flows = True planner.allow_empty_flows = True

View file

@ -40,11 +40,11 @@ def check_permissions(
sections/settings.html#oidc-after-userlogin-hook""" sections/settings.html#oidc-after-userlogin-hook"""
provider = client_related_provider(client) provider = client_related_provider(client)
if not provider: if not provider:
return redirect("passbook_providers_oauth:oauth2-permission-denied") return redirect("passbook_flows:denied")
try: try:
application = provider.application application = provider.application
except Application.DoesNotExist: except Application.DoesNotExist:
return redirect("passbook_providers_oauth:oauth2-permission-denied") return redirect("passbook_flows:denied")
LOGGER.debug( LOGGER.debug(
"Checking permissions for application", user=user, application=application "Checking permissions for application", user=user, application=application
) )
@ -56,7 +56,7 @@ def check_permissions(
if not result.passing: if not result.passing:
for policy_message in result.messages: for policy_message in result.messages:
messages.error(request, policy_message) messages.error(request, policy_message)
return redirect("passbook_providers_oauth:oauth2-permission-denied") return redirect("passbook_flows:denied")
plan: FlowPlan = request.session[SESSION_KEY_PLAN] plan: FlowPlan = request.session[SESSION_KEY_PLAN]
Event.new( Event.new(

View file

@ -1,7 +1,7 @@
"""passbook OIDC Views""" """passbook OIDC Views"""
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse, JsonResponse from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, reverse from django.shortcuts import get_object_or_404, reverse
from django.views import View from django.views import View
from oidc_provider.lib.endpoints.authorize import AuthorizeEndpoint from oidc_provider.lib.endpoints.authorize import AuthorizeEndpoint
from oidc_provider.lib.utils.common import get_issuer, get_site_url from oidc_provider.lib.utils.common import get_issuer, get_site_url
@ -41,11 +41,11 @@ class AuthorizationFlowInitView(PolicyAccessMixin, LoginRequiredMixin, View):
try: try:
application = self.provider_to_application(provider) application = self.provider_to_application(provider)
except Application.DoesNotExist: except Application.DoesNotExist:
return redirect("passbook_providers_oauth:oauth2-permission-denied") return self.handle_no_permission_authorized()
# Check permissions # Check permissions
result = self.user_has_access(application) result = self.user_has_access(application)
if not result.passing: if not result.passing:
return redirect("passbook_providers_oauth:oauth2-permission-denied") return self.handle_no_permission_authorized()
# Extract params so we can save them in the plan context # Extract params so we can save them in the plan context
endpoint = AuthorizeEndpoint(request) endpoint = AuthorizeEndpoint(request)
# Regardless, we start the planner and return to it # Regardless, we start the planner and return to it

View file

@ -2,7 +2,6 @@
from typing import Optional from typing import Optional
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.core.validators import URLValidator from django.core.validators import URLValidator
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render, reverse from django.shortcuts import get_object_or_404, redirect, render, reverse
@ -54,8 +53,10 @@ class SAMLSSOView(LoginRequiredMixin, PolicyAccessMixin, View):
self.provider: SAMLProvider = get_object_or_404( self.provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=self.application.provider_id SAMLProvider, pk=self.application.provider_id
) )
if not request.user.is_authenticated:
return self.handle_no_permission()
if not self.user_has_access(self.application).passing: if not self.user_has_access(self.application).passing:
raise PermissionDenied() return self.handle_no_permission_authorized()
# Call the method handler, which checks the SAML Request # Call the method handler, which checks the SAML Request
method_response = super().dispatch(request, *args, application_slug, **kwargs) method_response = super().dispatch(request, *args, application_slug, **kwargs)
if method_response: if method_response: