all: general maintenance, prepare for pyright

This commit is contained in:
Jens Langhammer 2020-02-18 22:12:51 +01:00
parent 865abc005a
commit 9267d0c1dd
12 changed files with 62 additions and 44 deletions

View file

@ -304,7 +304,9 @@ class PropertyMapping(UUIDModel):
form = "" form = ""
objects = InheritanceManager() objects = InheritanceManager()
def evaluate(self, user: User, request: HttpRequest, **kwargs) -> Any: def evaluate(
self, user: Optional[User], request: Optional[HttpRequest], **kwargs
) -> Any:
"""Evaluate `self.expression` using `**kwargs` as Context.""" """Evaluate `self.expression` using `**kwargs` as Context."""
try: try:
expression = NATIVE_ENVIRONMENT.from_string(self.expression) expression = NATIVE_ENVIRONMENT.from_string(self.expression)

View file

@ -19,7 +19,7 @@ from structlog import get_logger
from passbook.audit.models import Event, EventAction from passbook.audit.models import Event, EventAction
from passbook.factors.otp.forms import OTPSetupForm from passbook.factors.otp.forms import OTPSetupForm
from passbook.factors.otp.utils import otpauth_url from passbook.factors.otp.utils import otpauth_url
from passbook.lib.boilerplate import NeverCacheMixin from passbook.lib.mixins import NeverCacheMixin
from passbook.lib.config import CONFIG from passbook.lib.config import CONFIG
OTP_SESSION_KEY = "passbook_factors_otp_key" OTP_SESSION_KEY = "passbook_factors_otp_key"

View file

@ -1,12 +0,0 @@
"""passbook django boilerplate code"""
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
class NeverCacheMixin:
"""Use never_cache as mixin for CBV"""
@method_decorator(never_cache)
def dispatch(self, *args, **kwargs):
"""Use never_cache as mixin for CBV"""
return super().dispatch(*args, **kwargs)

View file

@ -1,4 +1,5 @@
"""passbook util mixins""" """passbook util mixins"""
from django.views.decorators.cache import never_cache
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
@ -10,3 +11,12 @@ class CSRFExemptMixin:
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
"""wrapper to apply @csrf_exempt to CBV""" """wrapper to apply @csrf_exempt to CBV"""
return super().dispatch(*args, **kwargs) return super().dispatch(*args, **kwargs)
class NeverCacheMixin:
"""Use never_cache as mixin for CBV"""
@method_decorator(never_cache)
def dispatch(self, *args, **kwargs):
"""Use never_cache as mixin for CBV"""
return super().dispatch(*args, **kwargs)

View file

@ -1,7 +1,7 @@
"""policy structures""" """policy structures"""
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, List from typing import TYPE_CHECKING, Tuple
from django.db.models import Model from django.db.models import Model
from django.http import HttpRequest from django.http import HttpRequest
@ -27,8 +27,8 @@ class PolicyRequest:
class PolicyResult: class PolicyResult:
"""Small data-class to hold policy results""" """Small data-class to hold policy results"""
passing: bool = False passing: bool
messages: List[str] = [] messages: Tuple[str]
def __init__(self, passing: bool, *messages: str): def __init__(self, passing: bool, *messages: str):
self.passing = passing self.passing = passing

View file

@ -15,13 +15,8 @@ from passbook.policies.engine import PolicyEngine
LOGGER = get_logger() LOGGER = get_logger()
def check_permissions( def client_related_provider(client: Client) -> Optional[Provider]:
request: HttpRequest, user: User, client: Client """Lookup related Application from Client"""
) -> Optional[HttpResponse]:
"""Check permissions, used for
https://django-oidc-provider.readthedocs.io/en/latest/
sections/settings.html#oidc-after-userlogin-hook"""
try:
# because oidc_provider is also used by app_gw, we can't be # because oidc_provider is also used by app_gw, we can't be
# sure an OpenIDPRovider instance exists. hence we look through all related models # sure an OpenIDPRovider instance exists. hence we look through all related models
# and choose the one that inherits from Provider, which is guaranteed to # and choose the one that inherits from Provider, which is guaranteed to
@ -31,8 +26,21 @@ def check_permissions(
for _, related in collector.data.items(): for _, related in collector.data.items():
related_object = next(iter(related)) related_object = next(iter(related))
if isinstance(related_object, Provider): if isinstance(related_object, Provider):
application = related_object.application return related_object
break return None
def check_permissions(
request: HttpRequest, user: User, client: Client
) -> Optional[HttpResponse]:
"""Check permissions, used for
https://django-oidc-provider.readthedocs.io/en/latest/
sections/settings.html#oidc-after-userlogin-hook"""
provider = client_related_provider(client)
if not provider:
return redirect("passbook_providers_oauth:oauth2-permission-denied")
try:
application = provider.application
except Application.DoesNotExist: except Application.DoesNotExist:
return redirect("passbook_providers_oauth:oauth2-permission-denied") return redirect("passbook_providers_oauth:oauth2-permission-denied")
LOGGER.debug( LOGGER.debug(

View file

@ -42,7 +42,11 @@ class AccessRequiredView(AccessMixin, View):
application = get_object_or_404( application = get_object_or_404(
Application, slug=self.kwargs["application"] Application, slug=self.kwargs["application"]
) )
self._provider = get_object_or_404(SAMLProvider, pk=application.provider_id) provider: SAMLProvider = get_object_or_404(
SAMLProvider, pk=application.provider_id
)
self._provider = provider
return self._provider
return self._provider return self._provider
def _has_access(self) -> bool: def _has_access(self) -> bool:

View file

@ -1,4 +1,5 @@
"""Core OAauth Views""" """Core OAauth Views"""
from typing import Callable, Optional
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@ -23,7 +24,7 @@ LOGGER = get_logger()
class OAuthClientMixin: class OAuthClientMixin:
"Mixin for getting OAuth client for a source." "Mixin for getting OAuth client for a source."
client_class = None client_class: Optional[Callable] = None
def get_client(self, source): def get_client(self, source):
"Get instance of the OAuth client for this source." "Get instance of the OAuth client for this source."

View file

@ -21,13 +21,15 @@ class SAMLSource(Source):
@property @property
def login_button(self): def login_button(self):
url = reverse_lazy("passbook_sources_saml:login", kwargs={"source": self.slug}) url = reverse_lazy(
"passbook_sources_saml:login", kwargs={"source_slug": self.slug}
)
return url, "", self.name return url, "", self.name
@property @property
def additional_info(self): def additional_info(self):
metadata_url = reverse_lazy( metadata_url = reverse_lazy(
"passbook_sources_saml:metadata", kwargs={"source": self} "passbook_sources_saml:metadata", kwargs={"source_slug": self}
) )
return f'<a href="{metadata_url}" class="btn btn-default btn-sm">Metadata Download</a>' return f'<a href="{metadata_url}" class="btn btn-default btn-sm">Metadata Download</a>'

View file

@ -4,8 +4,8 @@ from django.urls import path
from passbook.sources.saml.views import ACSView, InitiateView, MetadataView, SLOView from passbook.sources.saml.views import ACSView, InitiateView, MetadataView, SLOView
urlpatterns = [ urlpatterns = [
path("<slug:source>/", InitiateView.as_view(), name="login"), path("<slug:source_slug>/", InitiateView.as_view(), name="login"),
path("<slug:source>/acs/", ACSView.as_view(), name="acs"), path("<slug:source_slug>/acs/", ACSView.as_view(), name="acs"),
path("<slug:source>/slo/", SLOView.as_view(), name="slo"), path("<slug:source_slug>/slo/", SLOView.as_view(), name="slo"),
path("<slug:source>/metadata/", MetadataView.as_view(), name="metadata"), path("<slug:source_slug>/metadata/", MetadataView.as_view(), name="metadata"),
] ]

View file

@ -24,9 +24,9 @@ from passbook.sources.saml.xml_render import get_authnrequest_xml
class InitiateView(View): class InitiateView(View):
"""Get the Form with SAML Request, which sends us to the IDP""" """Get the Form with SAML Request, which sends us to the IDP"""
def get(self, request: HttpRequest, source: str) -> HttpResponse: def get(self, request: HttpRequest, source_slug: str) -> HttpResponse:
"""Replies with an XHTML SSO Request.""" """Replies with an XHTML SSO Request."""
source: SAMLSource = get_object_or_404(SAMLSource, slug=source) source: SAMLSource = get_object_or_404(SAMLSource, slug=source_slug)
if not source.enabled: if not source.enabled:
raise Http404 raise Http404
sso_destination = request.GET.get("next", None) sso_destination = request.GET.get("next", None)
@ -56,9 +56,9 @@ class InitiateView(View):
class ACSView(View): class ACSView(View):
"""AssertionConsumerService, consume assertion and log user in""" """AssertionConsumerService, consume assertion and log user in"""
def post(self, request: HttpRequest, source: str) -> HttpResponse: def post(self, request: HttpRequest, source_slug: str) -> HttpResponse:
"""Handles a POSTed SSO Assertion and logs the user in.""" """Handles a POSTed SSO Assertion and logs the user in."""
source: SAMLSource = get_object_or_404(SAMLSource, slug=source) source: SAMLSource = get_object_or_404(SAMLSource, slug=source_slug)
if not source.enabled: if not source.enabled:
raise Http404 raise Http404
# sso_session = request.POST.get('RelayState', None) # sso_session = request.POST.get('RelayState', None)
@ -74,9 +74,9 @@ class ACSView(View):
class SLOView(View): class SLOView(View):
"""Single-Logout-View""" """Single-Logout-View"""
def dispatch(self, request: HttpRequest, source: str) -> HttpResponse: def dispatch(self, request: HttpRequest, source_slug: str) -> HttpResponse:
"""Replies with an XHTML SSO Request.""" """Replies with an XHTML SSO Request."""
source: SAMLSource = get_object_or_404(SAMLSource, slug=source) source: SAMLSource = get_object_or_404(SAMLSource, slug=source_slug)
if not source.enabled: if not source.enabled:
raise Http404 raise Http404
logout(request) logout(request)
@ -93,9 +93,9 @@ class SLOView(View):
class MetadataView(View): class MetadataView(View):
"""Return XML Metadata for IDP""" """Return XML Metadata for IDP"""
def dispatch(self, request: HttpRequest, source: str) -> HttpResponse: def dispatch(self, request: HttpRequest, source_slug: str) -> HttpResponse:
"""Replies with the XML Metadata SPSSODescriptor.""" """Replies with the XML Metadata SPSSODescriptor."""
source: SAMLSource = get_object_or_404(SAMLSource, slug=source) source: SAMLSource = get_object_or_404(SAMLSource, slug=source_slug)
entity_id = get_entity_id(request, source) entity_id = get_entity_id(request, source)
return render_xml( return render_xml(
request, request,

3
pyrightconfig.json Normal file
View file

@ -0,0 +1,3 @@
{
"reportMissingTypeStubs": false
}