flows: cleanup denied view, use everywhere
This commit is contained in:
parent
76e2ba4764
commit
bead19c64c
|
@ -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>
|
||||||
|
|
|
@ -1,9 +1,36 @@
|
||||||
{% extends 'login/base.html' %}
|
{% extends 'base/skeleton.html' %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load passbook_utils %}
|
{% load passbook_utils %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<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 %}
|
{% block card %}
|
||||||
<form method="POST" class="pf-c-form">
|
<form method="POST" class="pf-c-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -19,3 +46,17 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% 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 %}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Reference in a new issue