From bd4058524720ddca03c80e6ba5d4b13c4bda2a05 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 1 Jul 2020 23:21:58 +0200 Subject: [PATCH] providers/samlv2: remove SAMLv2 from master --- passbook/providers/samlv2/__init__.py | 0 passbook/providers/samlv2/apps.py | 11 --- passbook/providers/samlv2/models.py | 0 passbook/providers/samlv2/saml/__init__.py | 0 passbook/providers/samlv2/saml/constants.py | 15 ---- passbook/providers/samlv2/saml/parser.py | 83 ------------------- passbook/providers/samlv2/saml/provider.py | 5 -- passbook/providers/samlv2/saml/utils.py | 24 ------ passbook/providers/samlv2/urls.py | 35 -------- passbook/providers/samlv2/views/__init__.py | 0 passbook/providers/samlv2/views/authorize.py | 6 -- passbook/providers/samlv2/views/base.py | 31 ------- .../providers/samlv2/views/idp_initiated.py | 6 -- passbook/providers/samlv2/views/slo.py | 10 --- passbook/providers/samlv2/views/sso.py | 41 --------- passbook/root/settings.py | 1 - 16 files changed, 268 deletions(-) delete mode 100644 passbook/providers/samlv2/__init__.py delete mode 100644 passbook/providers/samlv2/apps.py delete mode 100644 passbook/providers/samlv2/models.py delete mode 100644 passbook/providers/samlv2/saml/__init__.py delete mode 100644 passbook/providers/samlv2/saml/constants.py delete mode 100644 passbook/providers/samlv2/saml/parser.py delete mode 100644 passbook/providers/samlv2/saml/provider.py delete mode 100644 passbook/providers/samlv2/saml/utils.py delete mode 100644 passbook/providers/samlv2/urls.py delete mode 100644 passbook/providers/samlv2/views/__init__.py delete mode 100644 passbook/providers/samlv2/views/authorize.py delete mode 100644 passbook/providers/samlv2/views/base.py delete mode 100644 passbook/providers/samlv2/views/idp_initiated.py delete mode 100644 passbook/providers/samlv2/views/slo.py delete mode 100644 passbook/providers/samlv2/views/sso.py diff --git a/passbook/providers/samlv2/__init__.py b/passbook/providers/samlv2/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/passbook/providers/samlv2/apps.py b/passbook/providers/samlv2/apps.py deleted file mode 100644 index ada8dd6ec..000000000 --- a/passbook/providers/samlv2/apps.py +++ /dev/null @@ -1,11 +0,0 @@ -"""passbook saml provider app config""" -from django.apps import AppConfig - - -class PassbookProviderSAMLv2Config(AppConfig): - """passbook samlv2 provider app config""" - - name = "passbook.providers.samlv2" - label = "passbook_providers_samlv2" - verbose_name = "passbook Providers.SAMLv2" - mountpoint = "application/samlv2/" diff --git a/passbook/providers/samlv2/models.py b/passbook/providers/samlv2/models.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/passbook/providers/samlv2/saml/__init__.py b/passbook/providers/samlv2/saml/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/passbook/providers/samlv2/saml/constants.py b/passbook/providers/samlv2/saml/constants.py deleted file mode 100644 index 0b657dfe5..000000000 --- a/passbook/providers/samlv2/saml/constants.py +++ /dev/null @@ -1,15 +0,0 @@ -"""SAML-related constants""" -NS_SAML_PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol" -NS_SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion" -NS_SIGNATURE = "http://www.w3.org/2000/09/xmldsig#" - -REQ_KEY_REQUEST = "SAMLRequest" -REQ_KEY_SIGNATURE = "Signature" - -SESSION_KEY = "passbook_saml_request" - -SAML_ATTRIB_ACS_URL = "AssertionConsumerServiceURL" -SAML_ATTRIB_DESTINATION = "Destination" -SAML_ATTRIB_ID = "ID" -SAML_ATTRIB_ISSUE_INSTANT = "IssueInstant" -SAML_ATTRIB_PROTOCOL_BINDING = "ProtocolBinding" diff --git a/passbook/providers/samlv2/saml/parser.py b/passbook/providers/samlv2/saml/parser.py deleted file mode 100644 index 1ffa04a69..000000000 --- a/passbook/providers/samlv2/saml/parser.py +++ /dev/null @@ -1,83 +0,0 @@ -"""SAML Request Parse/builder""" -from typing import TYPE_CHECKING, Optional - -from defusedxml import ElementTree -from signxml import XMLVerifier - -from passbook.crypto.models import CertificateKeyPair -from passbook.providers.samlv2.saml.constants import ( - NS_SAML_ASSERTION, - NS_SAML_PROTOCOL, - SAML_ATTRIB_ACS_URL, - SAML_ATTRIB_DESTINATION, - SAML_ATTRIB_ID, - SAML_ATTRIB_ISSUE_INSTANT, - SAML_ATTRIB_PROTOCOL_BINDING, -) -from passbook.providers.samlv2.saml.utils import decode_base64_and_inflate - -if TYPE_CHECKING: - from xml.etree.ElementTree import Element # nosec - - -# pylint: disable=too-many-instance-attributes -class SAMLRequest: - """SAML Request data class, parse raw base64-encoded data, checks signature and more""" - - _root: "Element" - - acs_url: str - destination: str - id: str - issue_instant: str - protocol_binding: str - - issuer: str - - is_signed: bool - _detached_signature: str - - def __init__(self): - self.acs_url = "" - self.destination = "" - # pylint: disable=invalid-name - self.id = "" - self.issue_instant = "" - self.protocol_binding = "" - - @staticmethod - def parse(raw: str, detached_signature: Optional[str] = None) -> "SAMLRequest": - """Prase SAML request from raw string, which can be base64 encoded and deflated. - Optionally accepts a detached_signature, as from a REDIRECT request.""" - decoded_xml = decode_base64_and_inflate(raw) - root = ElementTree.fromstring(decoded_xml) - req = SAMLRequest() - req._root = root # pylint: disable=protected-access - # Verify the root element's tag - _expected_tag = f"{{{NS_SAML_PROTOCOL}}}AuthnRequest" - if root.tag != _expected_tag: - raise ValueError( - f"Invalid root tag, got '{root.tag}', expected '{_expected_tag}." - ) - req.acs_url = root.attrib[SAML_ATTRIB_ACS_URL] - req.destination = root.attrib[SAML_ATTRIB_DESTINATION] - req.id = root.attrib[SAML_ATTRIB_ID] - req.issue_instant = root.attrib[SAML_ATTRIB_ISSUE_INSTANT] - req.protocol_binding = root.attrib[SAML_ATTRIB_PROTOCOL_BINDING] - req.issuer = root.find(f"{{{NS_SAML_ASSERTION}}}Issuer").text - # Check if this Request is signed - if detached_signature: - # pylint: disable=protected-access - req._detached_signature = detached_signature - return req - - def verify_signature(self, keypair: CertificateKeyPair): - """Verify signature of SAML Request. - Raises `cryptography.exceptions.InvalidSignature` on validaton failure.""" - verifier = XMLVerifier() - if self._detached_signature: - verifier.verify( - self._detached_signature, x509_cert=keypair.certificate_data - ) - else: - verifier.verify(self._root, x509_cert=keypair.certificate_data) diff --git a/passbook/providers/samlv2/saml/provider.py b/passbook/providers/samlv2/saml/provider.py deleted file mode 100644 index fef98d4e5..000000000 --- a/passbook/providers/samlv2/saml/provider.py +++ /dev/null @@ -1,5 +0,0 @@ -"""SAML Provider logic""" - - -class SAMLProvider: - """SAML Provider""" diff --git a/passbook/providers/samlv2/saml/utils.py b/passbook/providers/samlv2/saml/utils.py deleted file mode 100644 index 9aac5246a..000000000 --- a/passbook/providers/samlv2/saml/utils.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Wrappers to de/encode and de/inflate strings""" -import base64 -import zlib - - -def decode_base64_and_inflate(encoded: str, encoding="utf-8") -> str: - """Base64 decode and ZLib decompress b64string""" - decoded_data = base64.b64decode(encoded) - try: - return zlib.decompress(decoded_data, -15).decode(encoding) - except zlib.error: - return decoded_data.decode(encoding) - - -def deflate_and_base64_encode(inflated: bytes, encoding="utf-8"): - """Base64 and ZLib Compress b64string""" - zlibbed_str = zlib.compress(inflated) - compressed_string = zlibbed_str[2:-4] - return base64.b64encode(compressed_string).decode(encoding) - - -def nice64(src): - """ Returns src base64-encoded and formatted nicely for our XML. """ - return base64.b64encode(src).decode("utf-8").replace("\n", "") diff --git a/passbook/providers/samlv2/urls.py b/passbook/providers/samlv2/urls.py deleted file mode 100644 index 2bd987d70..000000000 --- a/passbook/providers/samlv2/urls.py +++ /dev/null @@ -1,35 +0,0 @@ -"""passbook samlv2 URLs""" -from django.urls import path - -from passbook.providers.samlv2.views import authorize, idp_initiated, slo, sso - -urlpatterns = [ - path( - "/authorize/", - authorize.AuthorizeView.as_view(), - name="authorize", - ), - path( - "/sso/redirect/", - sso.SAMLRedirectBindingView.as_view(), - name="sso-redirect", - ), - path( - "/sso/post/", sso.SAMLPostBindingView.as_view(), name="sso-post", - ), - path( - "/slo/redirect/", - slo.SAMLRedirectBindingView.as_view(), - name="slo-redirect", - ), - path( - "/slo/redirect/", - slo.SAMLPostBindingView.as_view(), - name="slo-post", - ), - path( - "/initiate/", - idp_initiated.IDPInitiatedView.as_view(), - name="initiate", - ), -] diff --git a/passbook/providers/samlv2/views/__init__.py b/passbook/providers/samlv2/views/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/passbook/providers/samlv2/views/authorize.py b/passbook/providers/samlv2/views/authorize.py deleted file mode 100644 index 9b000d00b..000000000 --- a/passbook/providers/samlv2/views/authorize.py +++ /dev/null @@ -1,6 +0,0 @@ -"""SAML Provider authorization view""" -from django.views.generic import FormView - - -class AuthorizeView(FormView): - """Authorization view""" diff --git a/passbook/providers/samlv2/views/base.py b/passbook/providers/samlv2/views/base.py deleted file mode 100644 index d25277bc9..000000000 --- a/passbook/providers/samlv2/views/base.py +++ /dev/null @@ -1,31 +0,0 @@ -"""SAML base views""" -from typing import Optional - -from django.http import HttpRequest, HttpResponse -from django.shortcuts import get_object_or_404 -from django.views import View - -from passbook.core.models import Application -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(PolicyAccessMixin, View): - """Base SAML View to resolve app_slug""" - - application: Application - - def setup(self, request: HttpRequest, *args, **kwargs): - View.setup(self, request, *args, **kwargs) - self.application = self.get_application(self.kwargs.get("app_slug")) - - def get_application(self, app_slug: str) -> Optional[Application]: - """Return application or raise 404""" - return get_object_or_404(Application, slug=app_slug) - - def handle_saml_request(self, request: SAMLRequest) -> HttpResponse: - """Handle SAML Request""" - self.request.SESSION[SESSION_KEY] = request - if self.application.skip_authorization: - pass diff --git a/passbook/providers/samlv2/views/idp_initiated.py b/passbook/providers/samlv2/views/idp_initiated.py deleted file mode 100644 index 1568f6eae..000000000 --- a/passbook/providers/samlv2/views/idp_initiated.py +++ /dev/null @@ -1,6 +0,0 @@ -"""IDP-Initiated Views""" -from django.views import View - - -class IDPInitiatedView(View): - """IDP-initiated Handler""" diff --git a/passbook/providers/samlv2/views/slo.py b/passbook/providers/samlv2/views/slo.py deleted file mode 100644 index f74730db9..000000000 --- a/passbook/providers/samlv2/views/slo.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Single Logout Views""" -from django.views import View - - -class SAMLPostBindingView(View): - """Handle SAML POST-type Requests""" - - -class SAMLRedirectBindingView(View): - """Handle SAML Redirect-type Requests""" diff --git a/passbook/providers/samlv2/views/sso.py b/passbook/providers/samlv2/views/sso.py deleted file mode 100644 index 1c563a881..000000000 --- a/passbook/providers/samlv2/views/sso.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Single Signon Views""" -from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest - -from passbook.providers.samlv2.saml.constants import REQ_KEY_REQUEST, REQ_KEY_SIGNATURE -from passbook.providers.samlv2.saml.parser import SAMLRequest -from passbook.providers.samlv2.views.base import BaseSAMLView - -# SAML Authentication flow in passbook -# - Parse and Verify SAML Request -# - Check access to application (this is done after parsing as it might take a few seconds) -# - Ask for user authorization (if required from Application) -# - Log Access to audit log -# - Create response with unique ID to protect against replay - - -class SAMLPostBindingView(BaseSAMLView): - """Handle SAML POST-type Requests""" - - # pylint: disable=unused-argument - def post(self, request: HttpRequest, app_slug: str) -> HttpResponse: - """Handle POST Requests""" - if REQ_KEY_REQUEST not in request.POST: - return HttpResponseBadRequest() - raw_saml_request = request.POST.get(REQ_KEY_REQUEST) - detached_signature = request.POST.get(REQ_KEY_SIGNATURE, None) - srq = SAMLRequest.parse(raw_saml_request, detached_signature) - return self.handle_saml_request(srq) - - -class SAMLRedirectBindingView(BaseSAMLView): - """Handle SAML Redirect-type Requests""" - - # pylint: disable=unused-argument - def get(self, request: HttpRequest, app_slug: str) -> HttpResponse: - """Handle GET Requests""" - if REQ_KEY_REQUEST not in request.GET: - return HttpResponseBadRequest() - raw_saml_request = request.GET.get(REQ_KEY_REQUEST) - detached_signature = request.GET.get(REQ_KEY_SIGNATURE, None) - srq = SAMLRequest.parse(raw_saml_request, detached_signature) - return self.handle_saml_request(srq) diff --git a/passbook/root/settings.py b/passbook/root/settings.py index 2ac5249f0..ba53846e0 100644 --- a/passbook/root/settings.py +++ b/passbook/root/settings.py @@ -92,7 +92,6 @@ INSTALLED_APPS = [ "passbook.providers.oauth.apps.PassbookProviderOAuthConfig", "passbook.providers.oidc.apps.PassbookProviderOIDCConfig", "passbook.providers.saml.apps.PassbookProviderSAMLConfig", - "passbook.providers.samlv2.apps.PassbookProviderSAMLv2Config", "passbook.recovery.apps.PassbookRecoveryConfig", "passbook.sources.ldap.apps.PassbookSourceLDAPConfig", "passbook.sources.oauth.apps.PassbookSourceOAuthConfig",