providers/*: use PolicyAccessMixin to simplify
This commit is contained in:
parent
310b31a8b7
commit
cc0b8164b0
|
@ -1,4 +1,6 @@
|
|||
"""passbook access helper classes"""
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http import HttpRequest
|
||||
from django.utils.translation import gettext as _
|
||||
|
@ -11,13 +13,16 @@ from passbook.policies.types import PolicyResult
|
|||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class AccessMixin:
|
||||
class BaseMixin:
|
||||
"""Base Mixin class, used to annotate View Member variables"""
|
||||
|
||||
request: HttpRequest
|
||||
|
||||
|
||||
class PolicyAccessMixin(BaseMixin):
|
||||
"""Mixin class for usage in Authorization views.
|
||||
Provider functions to check application access, etc"""
|
||||
|
||||
# request is set by view but since this Mixin has no base class
|
||||
request: HttpRequest = None
|
||||
|
||||
def provider_to_application(self, provider: Provider) -> Application:
|
||||
"""Lookup application assigned to provider, throw error if no application assigned"""
|
||||
try:
|
||||
|
@ -32,9 +37,20 @@ class AccessMixin:
|
|||
)
|
||||
raise exc
|
||||
|
||||
def user_has_access(self, application: Application, user: User) -> PolicyResult:
|
||||
def user_has_access(
|
||||
self, application: Application, user: Optional[User] = None
|
||||
) -> PolicyResult:
|
||||
"""Check if user has access to application."""
|
||||
LOGGER.debug("Checking permissions", user=user, application=application)
|
||||
policy_engine = PolicyEngine(application, user, self.request)
|
||||
user = user or self.request.user
|
||||
policy_engine = PolicyEngine(
|
||||
application, user or self.request.user, self.request
|
||||
)
|
||||
policy_engine.build()
|
||||
return policy_engine.result
|
||||
result = policy_engine.result
|
||||
LOGGER.debug(
|
||||
"AccessMixin user_has_access", user=user, app=application, result=result,
|
||||
)
|
||||
if not result.passing:
|
||||
for message in result.messages:
|
||||
messages.error(self.request, _(message))
|
||||
return result
|
|
@ -1,5 +1,4 @@
|
|||
"""passbook OAuth2 Views"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
|
@ -11,7 +10,6 @@ from structlog import get_logger
|
|||
|
||||
from passbook.audit.models import Event, EventAction
|
||||
from passbook.core.models import Application
|
||||
from passbook.core.views.access import AccessMixin
|
||||
from passbook.flows.models import in_memory_stage
|
||||
from passbook.flows.planner import (
|
||||
PLAN_CONTEXT_APPLICATION,
|
||||
|
@ -21,6 +19,7 @@ from passbook.flows.planner import (
|
|||
from passbook.flows.stage import StageView
|
||||
from passbook.flows.views import SESSION_KEY_PLAN
|
||||
from passbook.lib.utils.urls import redirect_with_qs
|
||||
from passbook.policies.mixins import PolicyAccessMixin
|
||||
from passbook.providers.oauth.models import OAuth2Provider
|
||||
from passbook.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
|
||||
|
||||
|
@ -38,7 +37,7 @@ PLAN_CONTEXT_NONCE = "nonce"
|
|||
PLAN_CONTEXT_SCOPE_DESCRIPTION = "scope_descriptions"
|
||||
|
||||
|
||||
class AuthorizationFlowInitView(AccessMixin, LoginRequiredMixin, View):
|
||||
class AuthorizationFlowInitView(PolicyAccessMixin, LoginRequiredMixin, View):
|
||||
"""OAuth2 Flow initializer, checks access to application and starts flow"""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
@ -51,10 +50,8 @@ class AuthorizationFlowInitView(AccessMixin, LoginRequiredMixin, View):
|
|||
except Application.DoesNotExist:
|
||||
return redirect("passbook_providers_oauth:oauth2-permission-denied")
|
||||
# Check permissions
|
||||
result = self.user_has_access(application, request.user)
|
||||
result = self.user_has_access(application)
|
||||
if not result.passing:
|
||||
for policy_message in result.messages:
|
||||
messages.error(request, policy_message)
|
||||
return redirect("passbook_providers_oauth:oauth2-permission-denied")
|
||||
# Regardless, we start the planner and return to it
|
||||
planner = FlowPlanner(provider.authorization_flow)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"""passbook OIDC Views"""
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, reverse
|
||||
|
@ -11,7 +10,6 @@ from oidc_provider.views import AuthorizeView
|
|||
from structlog import get_logger
|
||||
|
||||
from passbook.core.models import Application
|
||||
from passbook.core.views.access import AccessMixin
|
||||
from passbook.flows.models import in_memory_stage
|
||||
from passbook.flows.planner import (
|
||||
PLAN_CONTEXT_APPLICATION,
|
||||
|
@ -22,6 +20,7 @@ from passbook.flows.planner import (
|
|||
from passbook.flows.stage import StageView
|
||||
from passbook.flows.views import SESSION_KEY_PLAN
|
||||
from passbook.lib.utils.urls import redirect_with_qs
|
||||
from passbook.policies.mixins import PolicyAccessMixin
|
||||
from passbook.providers.oidc.models import OpenIDProvider
|
||||
from passbook.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
|
||||
|
||||
|
@ -31,7 +30,7 @@ PLAN_CONTEXT_PARAMS = "params"
|
|||
PLAN_CONTEXT_SCOPES = "scopes"
|
||||
|
||||
|
||||
class AuthorizationFlowInitView(AccessMixin, LoginRequiredMixin, View):
|
||||
class AuthorizationFlowInitView(PolicyAccessMixin, LoginRequiredMixin, View):
|
||||
"""OIDC Flow initializer, checks access to application and starts flow"""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
@ -44,10 +43,8 @@ class AuthorizationFlowInitView(AccessMixin, LoginRequiredMixin, View):
|
|||
except Application.DoesNotExist:
|
||||
return redirect("passbook_providers_oauth:oauth2-permission-denied")
|
||||
# Check permissions
|
||||
result = self.user_has_access(application, request.user)
|
||||
result = self.user_has_access(application)
|
||||
if not result.passing:
|
||||
for policy_message in result.messages:
|
||||
messages.error(request, policy_message)
|
||||
return redirect("passbook_providers_oauth:oauth2-permission-denied")
|
||||
# Extract params so we can save them in the plan context
|
||||
endpoint = AuthorizeEndpoint(request)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
"""passbook SAML IDP Views"""
|
||||
from typing import Optional
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.validators import URLValidator
|
||||
|
@ -9,7 +8,6 @@ from django.http import HttpRequest, HttpResponse
|
|||
from django.shortcuts import get_object_or_404, redirect, render, reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views import View
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from signxml.util import strip_pem_header
|
||||
|
@ -28,7 +26,7 @@ from passbook.flows.views import SESSION_KEY_PLAN
|
|||
from passbook.lib.utils.template import render_to_string
|
||||
from passbook.lib.utils.urls import redirect_with_qs
|
||||
from passbook.lib.views import bad_request_message
|
||||
from passbook.policies.engine import PolicyEngine
|
||||
from passbook.policies.mixins import PolicyAccessMixin
|
||||
from passbook.providers.saml.exceptions import CannotHandleAssertion
|
||||
from passbook.providers.saml.models import SAMLBindings, SAMLProvider
|
||||
from passbook.providers.saml.processors.types import SAMLResponseParams
|
||||
|
@ -42,34 +40,13 @@ SESSION_KEY_RELAY_STATE = "RelayState"
|
|||
SESSION_KEY_PARAMS = "SAMLParams"
|
||||
|
||||
|
||||
class SAMLAccessMixin:
|
||||
"""SAML base access mixin, checks access to an application based on its policies"""
|
||||
|
||||
request: HttpRequest
|
||||
application: Application
|
||||
provider: SAMLProvider
|
||||
|
||||
def _has_access(self) -> bool:
|
||||
"""Check if user has access to application, add an error if not"""
|
||||
policy_engine = PolicyEngine(self.application, self.request.user, self.request)
|
||||
policy_engine.build()
|
||||
result = policy_engine.result
|
||||
LOGGER.debug(
|
||||
"SAMLFlowInit _has_access",
|
||||
user=self.request.user,
|
||||
app=self.application,
|
||||
result=result,
|
||||
)
|
||||
if not result.passing:
|
||||
for message in result.messages:
|
||||
messages.error(self.request, _(message))
|
||||
return result.passing
|
||||
|
||||
|
||||
class SAMLSSOView(LoginRequiredMixin, SAMLAccessMixin, View):
|
||||
class SAMLSSOView(LoginRequiredMixin, PolicyAccessMixin, View):
|
||||
""""SAML SSO Base View, which plans a flow and injects our final stage.
|
||||
Calls get/post handler."""
|
||||
|
||||
application: Application
|
||||
provider: SAMLProvider
|
||||
|
||||
def dispatch(
|
||||
self, request: HttpRequest, *args, application_slug: str, **kwargs
|
||||
) -> HttpResponse:
|
||||
|
@ -77,7 +54,7 @@ class SAMLSSOView(LoginRequiredMixin, SAMLAccessMixin, View):
|
|||
self.provider: SAMLProvider = get_object_or_404(
|
||||
SAMLProvider, pk=self.application.provider_id
|
||||
)
|
||||
if not self._has_access():
|
||||
if not self.user_has_access(self.application):
|
||||
raise PermissionDenied()
|
||||
# Call the method handler, which checks the SAML Request
|
||||
method_response = super().dispatch(request, *args, application_slug, **kwargs)
|
||||
|
|
|
@ -6,12 +6,12 @@ from django.shortcuts import get_object_or_404
|
|||
from django.views import View
|
||||
|
||||
from passbook.core.models import Application
|
||||
from passbook.core.views.access import AccessMixin
|
||||
from passbook.policies.mixins import PolicyAccessMixin
|
||||
from passbook.providers.samlv2.saml.constants import SESSION_KEY
|
||||
from passbook.providers.samlv2.saml.parser import SAMLRequest
|
||||
|
||||
|
||||
class BaseSAMLView(AccessMixin, View):
|
||||
class BaseSAMLView(PolicyAccessMixin, View):
|
||||
"""Base SAML View to resolve app_slug"""
|
||||
|
||||
application: Application
|
||||
|
|
Reference in a new issue