2019-11-07 16:02:56 +00:00
|
|
|
"""saml sp views"""
|
|
|
|
import base64
|
|
|
|
|
|
|
|
from defusedxml import ElementTree
|
|
|
|
from django.contrib.auth import login, logout
|
2019-11-07 16:35:25 +00:00
|
|
|
from django.http import HttpRequest, HttpResponse, Http404
|
2019-11-07 16:02:56 +00:00
|
|
|
from django.shortcuts import get_object_or_404, redirect, render, reverse
|
|
|
|
from django.utils.decorators import method_decorator
|
|
|
|
from django.views import View
|
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
|
|
|
|
|
|
from passbook.providers.saml.base import get_random_id, get_time_string
|
|
|
|
from passbook.providers.saml.utils import nice64
|
|
|
|
from passbook.providers.saml.views import render_xml
|
|
|
|
from passbook.sources.saml.models import SAMLSource
|
|
|
|
from passbook.sources.saml.utils import (_get_user_from_response,
|
|
|
|
build_full_url, get_entity_id)
|
|
|
|
from passbook.sources.saml.xml_render import get_authnrequest_xml
|
|
|
|
|
|
|
|
|
|
|
|
class InitiateView(View):
|
|
|
|
"""Get the Form with SAML Request, which sends us to the IDP"""
|
|
|
|
|
|
|
|
def get(self, request: HttpRequest, source: str) -> HttpResponse:
|
|
|
|
"""Replies with an XHTML SSO Request."""
|
|
|
|
source: SAMLSource = get_object_or_404(SAMLSource, slug=source)
|
2019-11-07 16:35:25 +00:00
|
|
|
if not source.enabled:
|
|
|
|
raise Http404
|
2019-11-07 16:02:56 +00:00
|
|
|
sso_destination = request.GET.get('next', None)
|
|
|
|
request.session['sso_destination'] = sso_destination
|
|
|
|
parameters = {
|
|
|
|
'ACS_URL': build_full_url('acs', request, source),
|
|
|
|
'DESTINATION': source.idp_url,
|
|
|
|
'AUTHN_REQUEST_ID': get_random_id(),
|
|
|
|
'ISSUE_INSTANT': get_time_string(),
|
|
|
|
'ISSUER': get_entity_id(request, source),
|
|
|
|
}
|
|
|
|
authn_req = get_authnrequest_xml(parameters, signed=False)
|
|
|
|
_request = nice64(str.encode(authn_req))
|
|
|
|
return render(request, 'saml/sp/login.html', {
|
|
|
|
'request_url': source.idp_url,
|
|
|
|
'request': _request,
|
|
|
|
'token': sso_destination,
|
|
|
|
'source': source
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
@method_decorator(csrf_exempt, name='dispatch')
|
|
|
|
class ACSView(View):
|
|
|
|
"""AssertionConsumerService, consume assertion and log user in"""
|
|
|
|
|
|
|
|
def post(self, request: HttpRequest, source: str) -> HttpResponse:
|
|
|
|
"""Handles a POSTed SSO Assertion and logs the user in."""
|
2019-11-07 16:35:25 +00:00
|
|
|
source: SAMLSource = get_object_or_404(SAMLSource, slug=source)
|
|
|
|
if not source.enabled:
|
|
|
|
raise Http404
|
2019-11-07 16:02:56 +00:00
|
|
|
# sso_session = request.POST.get('RelayState', None)
|
|
|
|
data = request.POST.get('SAMLResponse', None)
|
|
|
|
response = base64.b64decode(data)
|
|
|
|
root = ElementTree.fromstring(response)
|
|
|
|
user = _get_user_from_response(root)
|
|
|
|
# attributes = _get_attributes_from_response(root)
|
|
|
|
login(request, user, backend='django.contrib.auth.backends.ModelBackend')
|
|
|
|
return redirect(reverse('passbook_core:overview'))
|
|
|
|
|
|
|
|
|
|
|
|
class SLOView(View):
|
|
|
|
"""Single-Logout-View"""
|
|
|
|
|
|
|
|
def dispatch(self, request: HttpRequest, source: str) -> HttpResponse:
|
|
|
|
"""Replies with an XHTML SSO Request."""
|
|
|
|
source: SAMLSource = get_object_or_404(SAMLSource, slug=source)
|
2019-11-07 16:35:25 +00:00
|
|
|
if not source.enabled:
|
|
|
|
raise Http404
|
2019-11-07 16:02:56 +00:00
|
|
|
logout(request)
|
|
|
|
return render(request, 'saml/sp/sso_single_logout.html', {
|
|
|
|
'idp_logout_url': source.idp_logout_url,
|
|
|
|
'autosubmit': source.auto_logout,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataView(View):
|
|
|
|
"""Return XML Metadata for IDP"""
|
|
|
|
|
|
|
|
def dispatch(self, request: HttpRequest, source: str) -> HttpResponse:
|
|
|
|
"""Replies with the XML Metadata SPSSODescriptor."""
|
|
|
|
source: SAMLSource = get_object_or_404(SAMLSource, slug=source)
|
|
|
|
entity_id = get_entity_id(request, source)
|
|
|
|
return render_xml(request, 'saml/sp/xml/spssodescriptor.xml', {
|
|
|
|
'acs_url': build_full_url('acs', request, source),
|
|
|
|
'entity_id': entity_id,
|
|
|
|
'cert_public_key': source.signing_cert,
|
|
|
|
})
|