flows: add SESSION_KEY_APPLICATION_PRE
whenever a user tries to access an application without being authenticated to passbook, we now show notice which application they are going to continue to.
This commit is contained in:
parent
a3baa100d4
commit
b452e751ea
|
@ -29,6 +29,7 @@ LOGGER = get_logger()
|
||||||
# Argument used to redirect user after login
|
# Argument used to redirect user after login
|
||||||
NEXT_ARG_NAME = "next"
|
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_GET = "passbook_flows_get"
|
SESSION_KEY_GET = "passbook_flows_get"
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,8 +199,14 @@ class FlowExecutorView(View):
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
"""Cancel current execution and return a redirect"""
|
"""Cancel current execution and return a redirect"""
|
||||||
if SESSION_KEY_PLAN in self.request.session:
|
keys_to_delete = [
|
||||||
del self.request.session[SESSION_KEY_PLAN]
|
SESSION_KEY_APPLICATION_PRE,
|
||||||
|
SESSION_KEY_PLAN,
|
||||||
|
SESSION_KEY_GET,
|
||||||
|
]
|
||||||
|
for key in keys_to_delete:
|
||||||
|
if key in self.request.session:
|
||||||
|
del self.request.session[key]
|
||||||
|
|
||||||
|
|
||||||
class FlowPermissionDeniedView(PermissionDeniedView):
|
class FlowPermissionDeniedView(PermissionDeniedView):
|
||||||
|
|
|
@ -3,12 +3,14 @@ from typing import Optional
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import AccessMixin
|
from django.contrib.auth.mixins import AccessMixin
|
||||||
|
from django.contrib.auth.views import redirect_to_login
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import redirect
|
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
|
||||||
|
|
||||||
from passbook.core.models import Application, Provider, User
|
from passbook.core.models import Application, Provider, User
|
||||||
|
from passbook.flows.views import SESSION_KEY_APPLICATION_PRE
|
||||||
from passbook.policies.engine import PolicyEngine
|
from passbook.policies.engine import PolicyEngine
|
||||||
from passbook.policies.types import PolicyResult
|
from passbook.policies.types import PolicyResult
|
||||||
|
|
||||||
|
@ -25,6 +27,15 @@ 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(self, application: Optional[Application] = None):
|
||||||
|
if application:
|
||||||
|
self.request.session[SESSION_KEY_APPLICATION_PRE] = application
|
||||||
|
return redirect_to_login(
|
||||||
|
self.request.get_full_path(),
|
||||||
|
self.get_login_url(),
|
||||||
|
self.get_redirect_field_name(),
|
||||||
|
)
|
||||||
|
|
||||||
def handle_no_permission_authorized(self) -> HttpResponse:
|
def handle_no_permission_authorized(self) -> HttpResponse:
|
||||||
"""Function called when user has no permissions but is authorized"""
|
"""Function called when user has no permissions but is authorized"""
|
||||||
# TODO: Remove this URL and render the view instead
|
# TODO: Remove this URL and render the view instead
|
||||||
|
|
|
@ -49,6 +49,10 @@ class AuthorizationFlowInitView(PolicyAccessMixin, LoginRequiredMixin, View):
|
||||||
application = self.provider_to_application(provider)
|
application = self.provider_to_application(provider)
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
return self.handle_no_permission_authorized()
|
return self.handle_no_permission_authorized()
|
||||||
|
# Check if user is unauthenticated, so we pass the application
|
||||||
|
# for the identification stage
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return self.handle_no_permission(application)
|
||||||
# Check permissions
|
# Check permissions
|
||||||
result = self.user_has_access(application)
|
result = self.user_has_access(application)
|
||||||
if not result.passing:
|
if not result.passing:
|
||||||
|
|
|
@ -42,6 +42,10 @@ class AuthorizationFlowInitView(PolicyAccessMixin, LoginRequiredMixin, View):
|
||||||
application = self.provider_to_application(provider)
|
application = self.provider_to_application(provider)
|
||||||
except Application.DoesNotExist:
|
except Application.DoesNotExist:
|
||||||
return self.handle_no_permission_authorized()
|
return self.handle_no_permission_authorized()
|
||||||
|
# Check if user is unauthenticated, so we pass the application
|
||||||
|
# for the identification stage
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return self.handle_no_permission(application)
|
||||||
# Check permissions
|
# Check permissions
|
||||||
result = self.user_has_access(application)
|
result = self.user_has_access(application)
|
||||||
if not result.passing:
|
if not result.passing:
|
||||||
|
|
|
@ -47,7 +47,7 @@ REQUEST_KEY_RELAY_STATE = "RelayState"
|
||||||
SESSION_KEY_AUTH_N_REQUEST = "authn_request"
|
SESSION_KEY_AUTH_N_REQUEST = "authn_request"
|
||||||
|
|
||||||
|
|
||||||
class SAMLSSOView(LoginRequiredMixin, PolicyAccessMixin, View):
|
class SAMLSSOView(PolicyAccessMixin, LoginRequiredMixin, View):
|
||||||
""""SAML SSO Base View, which plans a flow and injects our final stage.
|
""""SAML SSO Base View, which plans a flow and injects our final stage.
|
||||||
Calls get/post handler."""
|
Calls get/post handler."""
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class SAMLSSOView(LoginRequiredMixin, PolicyAccessMixin, View):
|
||||||
SAMLProvider, pk=self.application.provider_id
|
SAMLProvider, pk=self.application.provider_id
|
||||||
)
|
)
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
return self.handle_no_permission()
|
return self.handle_no_permission(self.application)
|
||||||
if not self.user_has_access(self.application).passing:
|
if not self.user_has_access(self.application).passing:
|
||||||
return self.handle_no_permission_authorized()
|
return self.handle_no_permission_authorized()
|
||||||
# Call the method handler, which checks the SAML Request
|
# Call the method handler, which checks the SAML Request
|
||||||
|
|
|
@ -12,6 +12,7 @@ from structlog import get_logger
|
||||||
from passbook.core.models import Source, User
|
from passbook.core.models import Source, User
|
||||||
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from passbook.flows.stage import StageView
|
from passbook.flows.stage import StageView
|
||||||
|
from passbook.flows.views import SESSION_KEY_APPLICATION_PRE
|
||||||
from passbook.stages.identification.forms import IdentificationForm
|
from passbook.stages.identification.forms import IdentificationForm
|
||||||
from passbook.stages.identification.models import IdentificationStage
|
from passbook.stages.identification.models import IdentificationStage
|
||||||
|
|
||||||
|
@ -34,6 +35,12 @@ class IdentificationStageView(FormView, StageView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
current_stage: IdentificationStage = self.executor.current_stage
|
current_stage: IdentificationStage = self.executor.current_stage
|
||||||
|
# If the user has been redirected to us whilst trying to access an
|
||||||
|
# application, SESSION_KEY_APPLICATION_PRE is set in the session
|
||||||
|
if SESSION_KEY_APPLICATION_PRE in self.request.session:
|
||||||
|
kwargs["application_pre"] = self.request.session[
|
||||||
|
SESSION_KEY_APPLICATION_PRE
|
||||||
|
]
|
||||||
# Check for related enrollment and recovery flow, add URL to view
|
# Check for related enrollment and recovery flow, add URL to view
|
||||||
if current_stage.enrollment_flow:
|
if current_stage.enrollment_flow:
|
||||||
kwargs["enroll_url"] = reverse(
|
kwargs["enroll_url"] = reverse(
|
||||||
|
|
|
@ -11,6 +11,13 @@
|
||||||
<div class="pf-c-login__main-body">
|
<div class="pf-c-login__main-body">
|
||||||
<form method="POST" class="pf-c-form">
|
<form method="POST" class="pf-c-form">
|
||||||
{% block above_form %}
|
{% block above_form %}
|
||||||
|
{% if application_pre %}
|
||||||
|
<p>
|
||||||
|
{% blocktrans with app_name=application_pre.name %}
|
||||||
|
Login to continue to <strong>{{ app_name }}</strong>.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% include 'partials/form.html' %}
|
{% include 'partials/form.html' %}
|
||||||
|
|
Reference in a new issue