provider/samlv2: more samlv2 progres
This commit is contained in:
parent
b40bffdf38
commit
179f0097c0
|
@ -3,6 +3,11 @@ NS_SAML_PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
|
||||||
NS_SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
NS_SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
||||||
NS_SIGNATURE = "http://www.w3.org/2000/09/xmldsig#"
|
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_ACS_URL = "AssertionConsumerServiceURL"
|
||||||
SAML_ATTRIB_DESTINATION = "Destination"
|
SAML_ATTRIB_DESTINATION = "Destination"
|
||||||
SAML_ATTRIB_ID = "ID"
|
SAML_ATTRIB_ID = "ID"
|
||||||
|
|
5
passbook/providers/samlv2/saml/provider.py
Normal file
5
passbook/providers/samlv2/saml/provider.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
"""SAML Provider logic"""
|
||||||
|
|
||||||
|
|
||||||
|
class SAMLProvider:
|
||||||
|
"""SAML Provider"""
|
|
@ -1,31 +1,34 @@
|
||||||
"""passbook samlv2 URLs"""
|
"""passbook samlv2 URLs"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from passbook.providers.samlv2.views import idp_initiated, slo, sso
|
from passbook.providers.samlv2.views import authorize, idp_initiated, slo, sso
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(
|
path(
|
||||||
"<slug:application>/sso/redirect/",
|
"<slug:app_slug>/authorize/",
|
||||||
|
authorize.AuthorizeView.as_view(),
|
||||||
|
name="authorize",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"<slug:app_slug>/sso/redirect/",
|
||||||
sso.SAMLRedirectBindingView.as_view(),
|
sso.SAMLRedirectBindingView.as_view(),
|
||||||
name="sso-redirect",
|
name="sso-redirect",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<slug:application>/sso/post/",
|
"<slug:app_slug>/sso/post/", sso.SAMLPostBindingView.as_view(), name="sso-post",
|
||||||
sso.SAMLPostBindingView.as_view(),
|
|
||||||
name="sso-post",
|
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<slug:application>/slo/redirect/",
|
"<slug:app_slug>/slo/redirect/",
|
||||||
slo.SAMLRedirectBindingView.as_view(),
|
slo.SAMLRedirectBindingView.as_view(),
|
||||||
name="slo-redirect",
|
name="slo-redirect",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<slug:application>/slo/redirect/",
|
"<slug:app_slug>/slo/redirect/",
|
||||||
slo.SAMLPostBindingView.as_view(),
|
slo.SAMLPostBindingView.as_view(),
|
||||||
name="slo-post",
|
name="slo-post",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<slug:application>/initiate/",
|
"<slug:app_slug>/initiate/",
|
||||||
idp_initiated.IDPInitiatedView.as_view(),
|
idp_initiated.IDPInitiatedView.as_view(),
|
||||||
name="initiate",
|
name="initiate",
|
||||||
),
|
),
|
||||||
|
|
6
passbook/providers/samlv2/views/authorize.py
Normal file
6
passbook/providers/samlv2/views/authorize.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
"""SAML Provider authorization view"""
|
||||||
|
from django.views.generic import FormView
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizeView(FormView):
|
||||||
|
"""Authorization view"""
|
31
passbook/providers/samlv2/views/base.py
Normal file
31
passbook/providers/samlv2/views/base.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
"""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.core.views.access import AccessMixin
|
||||||
|
from passbook.providers.samlv2.saml.constants import SESSION_KEY
|
||||||
|
from passbook.providers.samlv2.saml.parser import SAMLRequest
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSAMLView(AccessMixin, 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
|
|
@ -1,10 +1,41 @@
|
||||||
"""Single Signon Views"""
|
"""Single Signon Views"""
|
||||||
from django.views import View
|
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(View):
|
class SAMLPostBindingView(BaseSAMLView):
|
||||||
"""Handle SAML POST-type Requests"""
|
"""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(View):
|
|
||||||
|
class SAMLRedirectBindingView(BaseSAMLView):
|
||||||
"""Handle SAML Redirect-type Requests"""
|
"""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)
|
||||||
|
|
Reference in a new issue