authentic/authentic2/authsaml2/saml2_endpoints.py

1746 lines
67 KiB
Python

"""SAML2.0 SP implementation"""
import logging
import urlparse
import urllib
import lasso
import authentic2.idp.views as idp_views
from django.conf import settings
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response
from django.http import HttpResponse, HttpResponseRedirect, \
HttpResponseBadRequest, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.template import RequestContext
from django.template.loader import render_to_string
from django.contrib.auth import login as auth_login, authenticate
from django.contrib.auth import logout as auth_logout
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.utils.translation import ugettext as _
from django.utils.http import urlquote
from authentic2.saml.common import get_idp_list, load_provider, \
return_saml2_request, get_saml2_request_message, get_saml2_query_request, \
get_saml2_post_response, soap_call, \
lookup_federation_by_name_identifier, get_authorization_policy, \
get_idp_options_policy, save_session, \
add_federation, load_session, send_soap_request, \
redirect_next, delete_session, SOAPException, \
remove_liberty_session_sp, get_session_index, get_soap_message, \
load_federation, save_manage, lookup_federation_by_user, \
get_manage_dump, get_saml2_metadata, create_saml2_server, \
maintain_liberty_session_on_service_provider, \
get_session_not_on_or_after, \
AUTHENTIC_STATUS_CODE_UNKNOWN_PROVIDER, \
AUTHENTIC_STATUS_CODE_INTERNAL_SERVER_ERROR, \
AUTHENTIC_STATUS_CODE_UNAUTHORIZED, \
get_sp_options_policy, get_entity_id
from authentic2.saml.models import LibertyProvider, LibertyFederation, \
LibertySessionSP, LibertySessionDump, LIBERTY_SESSION_DUMP_KIND_SP, \
save_key_values, NAME_ID_FORMATS, LibertySession, \
get_and_delete_key_values
from authentic2.saml.saml2utils import authnresponse_checking, \
get_attributes_from_assertion
from authentic2.idp.saml.saml2_endpoints import return_logout_error
from authentic2.authsaml2.utils import error_page, register_next_target, \
register_request_id, get_registered_url, save_federation_temp, \
load_federation_temp
from authentic2.authsaml2 import signals
from authentic2.authsaml2.backends import AuthSAML2PersistentBackend
from authentic2.utils import cache_and_validate, flush_django_session
__logout_redirection_timeout = getattr(settings, 'IDP_LOGOUT_TIMEOUT', 600)
logger = logging.getLogger(__name__)
metadata_map = (
('AssertionConsumerService',
lasso.SAML2_METADATA_BINDING_ARTIFACT,
'/singleSignOnArtifact'),
('AssertionConsumerService',
lasso.SAML2_METADATA_BINDING_POST,
'/singleSignOnPost'),
('SingleLogoutService',
lasso.SAML2_METADATA_BINDING_REDIRECT,
'/singleLogout', '/singleLogoutReturn'),
('SingleLogoutService',
lasso.SAML2_METADATA_BINDING_SOAP,
'/singleLogoutSOAP'),
('ManageNameIDService',
lasso.SAML2_METADATA_BINDING_SOAP,
'/manageNameIdSOAP'),
('ManageNameIDService',
lasso.SAML2_METADATA_BINDING_REDIRECT,
'/manageNameId', '/manageNameIdReturn'),
)
metadata_options = {'key': settings.SAML_SIGNATURE_PUBLIC_KEY}
try:
if settings.SHOW_DISCO_IN_MD:
metadata_options['disco'] = ('/discoveryReturn', )
except:
pass
@cache_and_validate(settings.LOCAL_METADATA_CACHE_TIMEOUT)
def metadata(request):
'''Endpoint to retrieve the metadata file'''
logger.info('metadata: return metadata')
return HttpResponse(get_metadata(request, request.path),
mimetype='text/xml')
##############################################################
#
# Discovery service Requester
# See Identity Provider Discovery Service Protocol and Profile
# OASIS Committee Specification 01
# 27 March 2008
#
##############################################################
def build_discovery_url(request):
target = None
returnIDParam = None
try:
target = settings.DISCO_SERVICE_NAME
returnIDParam = settings.DISCO_RETURN_ID_PARAM
except:
logger.error('build_discovery_url: missing parameter in settings')
return None
_return = urlquote(request.build_absolute_uri(reverse(disco_response)))
query = 'entityID=%s&return=%s&returnIDParam=%s' \
% (urlquote(request.build_absolute_uri(reverse(metadata))),
_return, returnIDParam)
try:
scheme, netloc, path, params, query, fragment = urlparse.urlparse(target)
return urlparse.urlunparse((scheme, netloc, path, params, query,
fragment))
except Exception, e:
logger.error('build_discovery_url: Exception %s' % str(e))
return None
def redirect_to_disco(request):
register_next_target(request)
url = build_discovery_url(request)
if not url:
return error_page(request,
_('redirect_to_disco: unable to build disco request'),
logger=logger)
return HttpResponseRedirect(url)
def disco_response(request):
if not request.method == "GET":
message = _('disco_response: HTTP request not supported %s' \
% request.method)
return error_page(request, message, logger=logger)
provider = request.GET.get(settings.DISCO_RETURN_ID_PARAM, '')
if provider:
request.session['prefered_idp'] = provider
logger.info('disco_response: discovered %s' % provider)
else:
logger.warn('disco_response: No provider discovered')
return HttpResponseRedirect(get_registered_url(request))
###
# sso
# @request
# @entity_id: Provider ID to request
#
# Single SignOn request initiated from SP UI
# Binding supported: Redirect
###
def sso(request, is_passive=None, force_authn=None, http_method=None):
'''Django view initiating an AuthnRequesst toward an identity provider.
Keyword arguments:
entity_id -- the SAMLv2 entity id identifier targeted by the
AuthnRequest, it should be resolvable to a metadata document.
is_passive -- whether to let the identity provider passively, i.e.
without user interaction, authenticate the user.
force_authn -- whether to ask the identity provider to authenticate the
user even if it is already authenticated.
'''
entity_id = request.REQUEST.get('entity_id')
# 1. Save the target page
logger.info('sso: save next url in session %s' \
% request.session.session_key)
register_next_target(request)
# 2. Init the server object
server = build_service_provider(request)
if not server:
return error_page(request,
_('sso: Service provider not configured'), logger=logger)
# 3. Define the provider or ask the user
if not entity_id:
providers_list = get_idp_list()
if not providers_list:
return error_page(request,
_('sso: Service provider not configured'), logger=logger)
if providers_list.count() == 1:
p = providers_list[0]
else:
logger.error('sso: No SAML2 identity provider selected')
return error_page(request,
_('sso: No SAML2 identity provider selected'),
logger=logger)
else:
logger.info('sso: sso with provider %s' % entity_id)
p = load_provider(request, entity_id, server=server, sp_or_idp='idp',
autoload=True)
if not p:
return error_page(request,
_('sso: The provider does not exist'), logger=logger)
# 4. Build authn request
login = lasso.Login(server)
if not login:
return error_page(request,
_('sso: Unable to create Login object'), logger=logger)
# Only redirect is necessary for the authnrequest
if not http_method:
http_method = server.getFirstHttpMethod(server.providers[p.entity_id],
lasso.MD_PROTOCOL_TYPE_SINGLE_SIGN_ON)
logger.debug('sso: \
No http method given. Method infered: %s' % http_method)
if http_method == lasso.HTTP_METHOD_NONE:
return error_page(request,
_('sso: %s does not have any supported SingleSignOn endpoint') \
% entity_id, logger=logger)
try:
login.initAuthnRequest(p.entity_id, http_method)
except lasso.Error, error:
return error_page(request,
_('sso: initAuthnRequest %s') % lasso.strError(error[0]),
logger=logger)
# 5. Request setting
if not setAuthnrequestOptions(p, login, force_authn, is_passive):
logger.error('sso: No policy defined')
return error_page(request, _('sso: No IdP policy defined'),
logger=logger)
try:
login.buildAuthnRequestMsg()
except lasso.Error, error:
return error_page(request,
_('SSO: buildAuthnRequestMsg %s') % lasso.strError(error[0]),
logger=logger)
# 6. Save the request ID (association with the target page)
logger.debug('sso: Authnrequest ID: %s' % login.request.iD)
logger.debug('sso: Save request id in the session %s' \
% request.session.session_key)
register_request_id(request, login.request.iD)
# 7. Redirect the user
logger.debug('sso: user redirection')
return return_saml2_request(request, login,
title=('AuthnRequest for %s' % entity_id))
###
# singleSignOnArtifact, singleSignOnPostOrRedirect
# @request
#
# Single SignOn Response
# Binding supported: Artifact, POST
###
def singleSignOnArtifact(request):
logger.info('singleSignOnArtifact: Binding Artifact processing begins...')
server = build_service_provider(request)
if not server:
return error_page(request,
_('singleSignOnArtifact: Service provider not configured'),
logger=logger)
# Load the provider metadata using the artifact
if request.method == 'GET':
logger.debug('singleSignOnArtifact: GET')
artifact = request.REQUEST.get('SAMLart')
else:
logger.debug('singleSignOnArtifact: POST')
artifact = request.POST.get('SAMLart')
logger.debug('singleSignOnArtifact: artifact %s' % artifact)
p = LibertyProvider.get_provider_by_samlv2_artifact(artifact)
p = load_provider(request, p.entity_id, server=server, sp_or_idp='idp')
logger.info('singleSignOnArtifact: provider %s loaded' % p.entity_id)
login = lasso.Login(server)
if not login:
return error_page(request,
_('singleSignOnArtifact: Unable to create Login object'),
logger=logger)
message = get_saml2_request_message(request)
if not message:
return error_page(request,
_('singleSignOnArtifact: No message given.'), logger=logger)
#logger.debug('singleSignOnArtifact: message %s' % message)
while True:
logger.debug('singleSignOnArtifact: Authnresponse processing')
try:
if request.method == 'GET':
login.initRequest(get_saml2_query_request(request),
lasso.HTTP_METHOD_ARTIFACT_GET)
else:
login.initRequest(artifact, lasso.HTTP_METHOD_ARTIFACT_POST)
break
except (lasso.ServerProviderNotFoundError,
lasso.ProfileUnknownProviderError):
logger.debug('singleSignOnArtifact: Unable to process \
Authnresponse -load another provider')
provider_id = login.remoteProviderId
provider_loaded = load_provider(request, provider_id,
server=server, sp_or_idp='idp')
if not provider_loaded:
message = _('singleSignOnArtifact: provider %r unknown') \
% provider_id
return error_page(request, message, logger=logger)
else:
logger.info('singleSignOnArtifact: \
provider %s loaded' % provider_id)
continue
except lasso.Error, error:
return error_page(request,
_('singleSignOnArtifact: initRequest %s') \
% lasso.strError(error[0]),
logger=logger)
try:
login.buildRequestMsg()
except lasso.Error, error:
return error_page(request,
_('singleSignOnArtifact: buildRequestMsg %s') \
% lasso.strError(error[0]), logger=logger)
# TODO: Client certificate
client_cert = None
try:
logger.info('singleSignOnArtifact: soap call to %s' % login.msgUrl)
logger.debug('singleSignOnArtifact: soap message %s' % login.msgBody)
soap_answer = soap_call(login.msgUrl,
login.msgBody, client_cert=client_cert)
except Exception:
return error_page(request,
_('singleSignOnArtifact: Failure to communicate \
with artifact resolver %r') % login.msgUrl,
logger=logger)
if not soap_answer:
return error_page(request,
_('singleSignOnArtifact: Artifact resolver at %r returned \
an empty response') % login.msgUrl,
logger=logger)
logger.debug('singleSignOnArtifact: soap answer %s' % soap_answer)
# If connexion over HTTPS, do not check signature?!
if login.msgUrl.startswith('https'):
logger.debug('singleSignOnArtifact: \
artifact solved over HTTPS - Signature Hint forbidden')
login.setSignatureVerifyHint(lasso.PROFILE_SIGNATURE_HINT_FORBID)
try:
login.processResponseMsg(soap_answer)
except lasso.Error, error:
return error_page(request,
_('singleSignOnArtifact: processResponseMsg raised %s') \
% lasso.strError(error[0]), logger=logger)
# TODO: Relay State
logger.info('singleSignOnArtifact: Binding artifact treatment terminated')
return sso_after_response(request, login, provider=p)
@csrf_exempt
def singleSignOnPost(request):
logger.info('singleSignOnPost: Binding POST processing begins...')
server = build_service_provider(request)
if not server:
return error_page(request,
_('singleSignOnPost: Service provider not configured'),
logger=logger)
login = lasso.Login(server)
if not login:
return error_page(request,
_('singleSignOnPost: Unable to create Login object'),
logger=logger)
# TODO: check messages = get_saml2_request_message(request)
# Binding POST
message = get_saml2_post_response(request)
if not message:
return error_page(request,
_('singleSignOnPost: No message given.'), logger=logger)
logger.debug('singleSignOnPost: message %s' % message)
''' Binding REDIRECT
According to: saml-profiles-2.0-os
The HTTP Redirect binding MUST NOT be used,
as the response will typically exceed the
URL length permitted by most user agents.
'''
# if not message:
# message = request.META.get('QUERY_STRING', '')
while True:
logger.debug('singleSignOnPost: Authnresponse processing')
try:
login.processAuthnResponseMsg(message)
break
except (lasso.ServerProviderNotFoundError,
lasso.ProfileUnknownProviderError):
logger.debug('singleSignOnPost: \
Unable to process Authnresponse - load another provider')
provider_id = login.remoteProviderId
provider_loaded = load_provider(request, provider_id,
server=server, sp_or_idp='idp')
if not provider_loaded:
message = _('singleSignOnPost: provider %r unknown' \
% provider_id)
return error_page(request, message, logger=logger)
else:
logger.info('singleSignOnPost: \
provider %s loaded' % provider_id)
continue
except lasso.Error, error:
logger.debug('singleSignOnPost: lasso error, login dump is %s' \
% login.dump())
return error_page(request,
_('singleSignOnPost: %s') % lasso.strError(error[0]),
logger=logger)
logger.info('singleSignOnPost: Binding POST treatment terminated')
return sso_after_response(request, login, provider=provider_loaded)
###
# sso_after_response
# @request
# @login
# @relay_state
#
# Post-authnrequest processing
###
def sso_after_response(request, login, relay_state=None, provider=None):
logger.info('sso_after_response: Authnresponse processing begins...')
subject_confirmation = request.build_absolute_uri().partition('?')[0]
saml_request_id = request.session.get('saml_request_id')
check = authnresponse_checking(login, subject_confirmation, logger, saml_request_id=saml_request_id)
if not check:
return error_page(request,
_('sso_after_response: error checking authn response'), logger=logger)
try:
login.acceptSso()
except lasso.Error, error:
return error_page(request,
_('sso_after_response: acceptSso raised %s') \
% lasso.strError(error[0]), logger=logger)
logger.info('sso_after_response: \
Assertion processing terminated with success')
attributes = get_attributes_from_assertion(login.assertion, logger)
# Register attributes in session for other applications
request.session['attributes'] = attributes
attrs = {}
for att_statement in login.assertion.attributeStatement:
for attribute in att_statement.attribute:
name = None
format = lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC
nickname = None
try:
name = attribute.name.decode('ascii')
except:
logger.warning('sso_after_response: error decoding name of \
attribute %s' % attribute.dump())
else:
try:
if attribute.nameFormat:
format = attribute.nameFormat.decode('ascii')
if attribute.friendlyName:
nickname = attribute.friendlyName
except Exception, e:
message = 'sso_after_response: name or format of an \
attribute failed to decode as ascii: %s due to %s'
logger.warning(message % (attribute.dump(), str(e)))
try:
if name:
if format:
if nickname:
key = (name, format, nickname)
else:
key = (name, format)
else:
key = (name)
attrs[key] = list()
for value in attribute.attributeValue:
content = [any.exportToXml() for any in value.any]
content = ''.join(content)
attrs[key].append(content.decode('utf8'))
except Exception, e:
message = 'sso_after_response: value of an \
attribute failed to decode as ascii: %s due to %s'
logger.warning(message % (attribute.dump(), str(e)))
if not 'multisource_attributes' in request.session:
request.session['multisource_attributes'] = dict()
request.\
session['multisource_attributes'][login.assertion.issuer.content] = \
list()
a8n = dict()
a8n['certificate_type'] = 'SAML2_assertion'
try:
a8n['nameid'] = \
login.assertion.subject.nameID.content
except:
pass
try:
a8n['subject_confirmation_method'] = \
login.assertion.subject.subjectConfirmation.method
except:
pass
try:
a8n['not_before'] = \
login.assertion.subject. \
subjectConfirmation.subjectConfirmationData.notBefore
except:
pass
try:
a8n['not_on_or_after'] = \
login.assertion.subject.subjectConfirmation. \
subjectConfirmationData.notOnOrAfter
except:
pass
try:
a8n['authn_context'] = \
login.assertion.authnStatement[0]. \
authnContext.authnContextClassRef
except:
pass
try:
a8n['authn_instant'] = \
login.assertion.authnStatement[0].authnInstant
except:
pass
a8n['attributes'] = attrs
request.\
session['multisource_attributes'][login.assertion.issuer.content].\
append(a8n)
logger.debug('sso_after_response: \
attributes in assertion %s from %s' \
% (str(attrs), login.assertion.issuer.content))
#authncontext
#Access control processing
decisions = signals.authz_decision.send(sender=None,
request=request, attributes=attributes, provider=provider)
if not decisions:
logger.debug('sso_after_response: No authorization function \
connected')
access_granted = True
one_message = False
for decision in decisions:
logger.debug('sso_after_response: authorization function %s' \
% decision[0].__name__)
dic = decision[1]
logger.debug('sso_after_response: decision is %s' % dic['authz'])
if 'message' in dic:
logger.debug('sso_after_response: with message %s' \
% dic['message'])
if not dic['authz']:
access_granted = False
if 'message' in dic:
one_message = True
messages.add_message(request, messages.ERROR, dic['message'])
if not access_granted:
if not one_message:
p = get_authorization_policy(provider)
messages.add_message(request, messages.ERROR,
p.default_denial_message)
return error_page(request,
logger=logger, default_message=False, timer=True)
#Access granted, now we deal with session management
policy = get_idp_options_policy(provider)
if not policy:
logger.error('sso_after_response: No policy defined')
return error_page(request,
_('sso_after_response: No IdP policy defined'), logger=logger)
user = request.user
url = get_registered_url(request)
if not 'saml_request_id' in request.session:
#IdP initiated
url = policy.back_url
if login.nameIdentifier.format == \
lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT \
and (policy is None \
or not policy.transient_is_persistent):
logger.info('sso_after_response: Transient nameID')
if policy.handle_transient == 'AUTHSAML2_UNAUTH_TRANSIENT_ASK_AUTH':
if not request.user.is_authenticated():
#XXX: To test!
logger.info('sso_after_response: Account linking required')
save_session(request, login,
kind=LIBERTY_SESSION_DUMP_KIND_SP)
logger.debug('sso_after_response: \
Register identity dump in session')
save_federation_temp(request, login, attributes=attributes)
maintain_liberty_session_on_service_provider(request, login)
return render_to_response('auth/saml2/account_linking.html',
context_instance=RequestContext(request))
logger.debug('sso_after_response: django session opened')
session_not_on_or_after = \
get_session_not_on_or_after(login.assertion)
if session_not_on_or_after:
request.session.set_expiry(session_not_on_or_after)
logger.debug('sso_after_response: session set to expire on \
%s by SessionNotOnOrAfter attribute',
session_not_on_or_after)
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
save_session(request, login, kind=LIBERTY_SESSION_DUMP_KIND_SP)
logger.info('sso_after_response: \
login processing ended with success - redirect to target')
return HttpResponseRedirect(url)
if policy.handle_transient == \
'AUTHSAML2_UNAUTH_TRANSIENT_OPEN_SESSION':
logger.info('sso_after_response: \
Opening session for transient with nameID')
logger.debug('sso_after_response: nameID %s' \
% login.nameIdentifier.dump())
user = authenticate(name_id=login.nameIdentifier)
if not user:
return error_page(request,
_('sso_after_response: \
No backend for temporary federation is configured'),
logger=logger)
auth_login(request, user)
logger.debug('sso_after_response: django session opened')
session_not_on_or_after = \
get_session_not_on_or_after(login.assertion)
if session_not_on_or_after:
request.session.set_expiry(session_not_on_or_after)
logger.debug('sso_after_response: session set to expire on \
%s by SessionNotOnOrAfter attribute',
session_not_on_or_after)
signals.auth_login.send(sender=None,
request=request, attributes=attributes)
logger.debug('sso_after_response: successful login signal sent')
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
save_session(request, login, kind=LIBERTY_SESSION_DUMP_KIND_SP)
logger.info('sso_after_response: \
login processing ended with success - redirect to target')
return HttpResponseRedirect(url)
return error_page(request, _('sso_after_response: \
Transient access policy: Configuration error'),
logger=logger)
elif login.nameIdentifier.format != \
lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT \
or (policy is not None and policy.transient_is_persistent):
#Consider that all kinds of nameId not transient are persistent.
logger.info('sso_after_response: persistent federation processing')
if policy is not None and policy.transient_is_persistent and \
login.nameIdentifier.format == \
lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT:
logger.info('sso_after_response: \
the nameId %s is transient the but option persistent '
'federation processing is selected' \
% login.nameIdentifier.dump())
if policy.persistent_identifier_attribute:
logger.info('sso_after_response: '
'%s is used as persistent identifier for federation' % \
policy.persistent_identifier_attribute)
identifier = None
for key in attributes:
if policy.persistent_identifier_attribute in key \
and attributes[key]:
identifier = attributes[key][0]
break
if identifier:
logger.info('sso_after_response: '
'%s value is %s' % \
(policy.persistent_identifier_attribute, identifier))
login.nameIdentifier.content = identifier
else:
logger.warn('sso_after_response: '
'%s not provided by the identity provider, '
'we continue with the nameID.' % \
policy.persistent_identifier_attribute)
else:
logger.info('sso_after_response: '
'No attribute declared as persistent identifier '
'NameID is used.')
login.nameIdentifier.format = \
lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT
login.nameIdentifier.nameQualifier = provider.entity_id
user = AuthSAML2PersistentBackend(). \
authenticate(name_id=login.nameIdentifier,
provider_id=login.remoteProviderId)
if not user and \
policy.handle_persistent == \
'AUTHSAML2_UNAUTH_PERSISTENT_CREATE_USER_PSEUDONYMOUS':
# Auto-create an user then do the authentication again
logger.info('sso_after_response: Account creation')
AuthSAML2PersistentBackend(). \
create_user(name_id=login.nameIdentifier,
provider_id=provider.entity_id)
user = AuthSAML2PersistentBackend(). \
authenticate(name_id=login.nameIdentifier,
provider_id=login.remoteProviderId)
if user:
auth_login(request, user)
logger.debug('sso_after_response: session opened')
signals.auth_login.send(sender=None,
request=request, attributes=attributes)
logger.debug('sso_after_response: \
signal sent that the session is opened')
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
save_session(request, login, kind=LIBERTY_SESSION_DUMP_KIND_SP)
maintain_liberty_session_on_service_provider(request, login)
logger.info('sso_after_response: \
Login processing ended with success - redirect to target')
return HttpResponseRedirect(url)
elif policy.handle_persistent == \
'AUTHSAML2_UNAUTH_PERSISTENT_ACCOUNT_LINKING_BY_AUTH':
'''Check if the user consent for federation has been given'''
if policy.force_user_consent \
and not login.response.consent in \
('urn:oasis:names:tc:SAML:2.0:consent:obtained',
'urn:oasis:names:tc:SAML:2.0:consent:prior',
'urn:oasis:names:tc:SAML:2.0:consent:current-explicit',
'urn:oasis:names:tc:SAML:2.0:consent:current-implicit'):
return error_page(request, _('sso_after_response: You were \
not asked your consent for account linking'),
logger=logger)
if request.user.is_authenticated():
logger.info('sso_after_response: Add federation')
add_federation(request.user, name_id=login.nameIdentifier,
provider_id=login.remoteProviderId)
return HttpResponseRedirect(url)
logger.info('sso_after_response: Account linking required')
save_session(request, login, kind=LIBERTY_SESSION_DUMP_KIND_SP)
logger.debug('sso_after_response: \
Register identity dump in session')
save_federation_temp(request, login, attributes=attributes)
maintain_liberty_session_on_service_provider(request, login)
return render_to_response('auth/saml2/account_linking.html',
context_instance=RequestContext(request))
return error_page(request,
_('sso_after_response: \
Persistent Account policy: Configuration error'), logger=logger)
return error_page(request,
_('sso_after_response: \
Transient access policy: NameId format not supported'), logger=logger)
#TODO: Relay state
###
# finish_federation
# @request
#
# Called after an account linking.
# TODO: add checkbox, create new account (settings option, user can choose)
# Create pseudonymous or user choose or only account linking
###
@csrf_exempt
def finish_federation(request):
logger.info('finish_federation: Return after account linking form filled')
if request.method == "POST":
form = AuthenticationForm(data=request.POST)
if form.is_valid():
logger.info('finish_federation: form valid')
server = build_service_provider(request)
if not server:
return error_page(request,
_('finish_federation: \
Service provider not configured'), logger=logger)
login = lasso.Login(server)
if not login:
return error_page(request,
_('finish_federation: \
Unable to create Login object'), logger=logger)
s = load_session(request, login, kind=LIBERTY_SESSION_DUMP_KIND_SP)
load_federation_temp(request, login)
if not login.session:
return error_page(request,
_('finish_federation: Error loading session.'),
logger=logger)
login.nameIdentifier = request.session['nameId']
logger.debug('finish_federation: nameID %s' % \
login.nameIdentifier.dump())
provider_id = None
if 'remoteProviderId' in request.session:
provider_id = request.session['remoteProviderId']
fed = add_federation(form.get_user(),
name_id=login.nameIdentifier,
provider_id=provider_id)
if not fed:
return error_page(request,
_('SSO/finish_federation: \
Error adding new federation for this user'),
logger=logger)
logger.info('finish_federation: federation added')
url = get_registered_url(request)
auth_login(request, form.get_user())
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
logger.debug('finish_federation: session opened')
attributes = []
if 'attributes' in request.session:
attributes = request.session['attributes']
signals.auth_login.send(sender=None,
request=request, attributes=attributes)
logger.debug('finish_federation: \
signal sent that the session is opened')
if s:
s.delete()
if login.session:
login.session.isDirty = True
if login.identity:
login.identity.isDirty = True
save_session(request, login, kind=LIBERTY_SESSION_DUMP_KIND_SP)
maintain_liberty_session_on_service_provider(request, login)
logger.info('finish_federation: \
Login processing ended with success - redirect to target')
return HttpResponseRedirect(url)
else:
# TODO: Error: login failed: message and count 3 attemps
logger.warning('finish_federation: \
form not valid - Try again! (Brute force?)')
return render_to_response('auth/saml2/account_linking.html',
context_instance=RequestContext(request))
else:
return error_page(request,
_('finish_federation: Unable to perform federation'),
logger=logger)
'''
Single Logout (SLO)
Initiated by SP or by IdP with SOAP or with Redirect
'''
def ko_icon(request):
return HttpResponseRedirect('%s/authentic2/images/ko.png' \
% settings.STATIC_URL)
def ok_icon(request):
return HttpResponseRedirect('%s/authentic2/images/ok.png' \
% settings.STATIC_URL)
def sp_slo(request, provider_id=None):
'''
To make another module call the SLO function.
Does not deal with the local django session.
'''
next = request.REQUEST.get('next')
logger.debug('sp_slo: provider_id in parameter %s' % str(provider_id))
if request.method == 'GET' and 'provider_id' in request.GET:
provider_id = request.GET.get('provider_id')
logger.debug('sp_slo: provider_id from GET %s' % str(provider_id))
if request.method == 'POST' and 'provider_id' in request.POST:
provider_id = request.POST.get('provider_id')
logger.debug('sp_slo: provider_id from POST %s' % str(provider_id))
if not provider_id:
logger.info('sp_slo: to initiate a slo we need a provider_id')
return redirect_next(request, next) or ko_icon(request)
logger.info('sp_slo: slo initiated with %(provider_id)s' \
% {'provider_id': provider_id})
server = create_server(request)
logout = lasso.Logout(server)
logger.info('sp_slo: sp_slo for %s' % provider_id)
load_session(request, logout, kind=LIBERTY_SESSION_DUMP_KIND_SP)
provider = load_provider(request, provider_id,
server=server, sp_or_idp='idp')
if not provider:
logger.error('sp_slo: sp_slo failed to load provider')
return redirect_next(request, next) or ko_icon(request)
policy = get_idp_options_policy(provider)
if not policy:
logger.error('sp_slo: No policy found for %s'\
% provider_id)
return redirect_next(request, next) or ko_icon(request)
if not policy.forward_slo:
logger.warn('sp_slo: slo asked for %s configured to not receive slo' \
% provider_id)
return redirect_next(request, next) or ko_icon(request)
if policy.enable_http_method_for_slo_request \
and policy.http_method_for_slo_request:
if policy.http_method_for_slo_request == lasso.HTTP_METHOD_SOAP:
logger.info('sp_slo: sp_slo by SOAP')
try:
logout.initRequest(None, lasso.HTTP_METHOD_SOAP)
except:
logger.exception('sp_slo: sp_slo init error')
return redirect_next(request, next) or ko_icon(request)
try:
logout.buildRequestMsg()
except:
logger.exception('sp_slo: sp_slo build error')
return redirect_next(request, next) or ko_icon(request)
try:
soap_response = send_soap_request(request, logout)
except:
logger.exception('sp_slo: sp_slo SOAP failure')
return redirect_next(request, next) or ko_icon(request)
logger.info('sp_slo: successful soap call')
return process_logout_response(request,
logout, soap_response, next)
else:
try:
logout.initRequest(None, lasso.HTTP_METHOD_REDIRECT)
except:
logger.exception('sp_slo: sp_slo init error')
return redirect_next(request, next) or ko_icon(request)
logout.msgRelayState = logout.request.id
try:
logout.buildRequestMsg()
except:
logger.exception('sp_slo: sp_slo build error')
return redirect_next(request, next) or ko_icon(request)
logger.info('sp_slo: sp_slo by redirect')
save_key_values(logout.request.id,
logout.dump(), provider_id, next)
return HttpResponseRedirect(logout.msgUrl)
try:
logout.initRequest(provider_id)
except lasso.ProfileMissingAssertionError:
logger.error('sp_slo: \
sp_slo failed because no sessions exists for %r' % provider_id)
return redirect_next(request, next) or ko_icon(request)
logout.msgRelayState = logout.request.id
try:
logout.buildRequestMsg()
except:
logger.exception('sp_slo: sp_slo misc error')
return redirect_next(request, next) or ko_icon(request)
# SOAP case
if logout.msgBody:
logger.info('sp_slo: sp_slo by SOAP')
try:
soap_response = send_soap_request(request, logout)
except:
logger.exception('sp_slo: sp_slo SOAP failure')
return redirect_next(request, next) or ko_icon(request)
return process_logout_response(request, logout, soap_response, next)
else:
logger.info('sp_slo: sp_slo by redirect')
save_key_values(logout.request.id, logout.dump(), provider_id, next)
return HttpResponseRedirect(logout.msgUrl)
def process_logout_response(request, logout, soap_response, next):
try:
logout.processResponseMsg(soap_response)
except:
logger.exception('process_logout_response: \
processResponseMsg raised an exception')
return redirect_next(request, next) or ko_icon(request)
else:
delete_session(request)
return redirect_next(request, next) or ok_icon(request)
def singleLogoutReturn(request):
'''
IdP response to a SLO SP initiated by redirect
'''
server = build_service_provider(request)
if not server:
logger.warn('singleLogoutReturn: Service provider not configured')
return ko_icon(request)
query = get_saml2_query_request(request)
if not query:
logger.warn('singleLogoutReturn: \
Unable to handle SLO by Redirect return without LogoutResponse')
return ko_icon(request)
logout = lasso.Logout(server)
if not logout:
logger.warn('singleLogoutReturn: Unable to create Login object')
return ko_icon(request)
load_session(request, logout, kind=LIBERTY_SESSION_DUMP_KIND_SP)
id = request.REQUEST.get('RelayState', None)
if not id:
logger.error('singleLogoutReturn: missing id argument')
return ko_icon(request)
logout_dump, provider_id_saved, next = get_and_delete_key_values(id)
provider_loaded = None
provider_id = None
while True:
try:
logout.processResponseMsg(query)
break
except (lasso.ServerProviderNotFoundError,
lasso.ProfileUnknownProviderError):
provider_id = logout.remoteProviderId
provider_loaded = load_provider(request, provider_id,
server=server, sp_or_idp='idp')
if not provider_loaded:
logger.warn('singleLogoutReturn: provider %r unknown' \
% provider_id)
return redirect_next(request, next) or ko_icon(request)
else:
continue
except lasso.Error, error:
return redirect_next(request, next) or ko_icon(request)
if provider_id != provider_id_saved:
logger.warn('singleLogoutReturn: provider mistmatch between %s and %s' \
% (provider_id, provider_id_saved))
return redirect_next(request, next) or ko_icon(request)
if logout.isSessionDirty:
if logout.session:
save_session(request, logout, kind=LIBERTY_SESSION_DUMP_KIND_SP)
else:
delete_session(request)
remove_liberty_session_sp(request)
return redirect_next(request, next) or ok_icon(request)
def slo_soap_as_idp(request, logout, session=None):
logger.debug('slo_soap_as_idp: start slo proxying to sp processing')
from authentic2.idp.saml import saml2_endpoints
error = saml2_endpoints. \
validate_logout_request(request, logout, idp=False)
if error:
return error
if not session:
key = request.session.session_key
else:
key = session.django_session_key
lib_sessions = LibertySession.objects.filter(
django_session_key=key)
if not lib_sessions:
logger.debug('slo_soap_as_idp: no sp session')
else:
server = saml2_endpoints.create_server(request)
logout2 = lasso.Logout(server)
for lib_session in lib_sessions:
logger.info('slo_soap_as_idp: logout to provider %s' \
% lib_session.provider_id)
p = load_provider(request, lib_session.provider_id,
server=server)
if p:
policy = get_sp_options_policy(p)
if not policy:
logger.error('slo_soap_as_idp: No policy found for %s' \
% lib_session.provider_id)
elif not policy.forward_slo:
logger.info('slo_soap_as_idp: %s configured to not \
reveive slo' % lib_session.provider_id)
else:
try:
session_dump = saml2_endpoints.build_session_dump([lib_session])
logout2.setSessionFromDump(session_dump.encode('utf8'))
logout2.initRequest(None, lasso.HTTP_METHOD_SOAP)
logout2.buildRequestMsg()
soap_response = send_soap_request(request, logout2)
except Exception, e:
logger.error('slo_soap_as_idp: error building \
request to provider %s due to %s' \
% (lib_session.provider_id, str(e)))
else:
try:
logout2.processResponseMsg(soap_response)
except Exception, e:
logger.error('slo_soap_as_idp: error received \
from provider %s due to %s' \
% (lib_session.provider_id, str(e)))
else:
logger.error('slo_soap_as_idp: unable to load provider %s' \
% lib_session.provider_id)
logger.debug('slo_soap_as_idp: end slo proxying to sp processing')
def common_processing_slo_idp_init_bindings(request, message):
server = build_service_provider(request)
if not server:
return http_response_forbidden_request('common_processing: '
'Service provider not configured'), None, None
logout = lasso.Logout(server)
if not logout:
return http_response_forbidden_request('common_processing: '
'Unable to create Logout object'), None, None
provider_loaded = None
while True:
try:
logout.processRequestMsg(message)
break
except (lasso.ServerProviderNotFoundError,
lasso.ProfileUnknownProviderError):
provider_id = logout.remoteProviderId
provider_loaded = load_provider(request, provider_id,
server=server, sp_or_idp='idp')
if not provider_loaded:
logger.warn('common_processing: provider %r unknown' \
% provider_id)
return return_logout_error(request, logout,
AUTHENTIC_STATUS_CODE_UNKNOWN_PROVIDER), None, None
else:
continue
except lasso.Error, error:
return return_logout_error(request, logout,
AUTHENTIC_STATUS_CODE_INTERNAL_SERVER_ERROR), None, None
policy = get_idp_options_policy(provider_loaded)
if not policy:
logger.error('common_processing: No policy found for %s'\
% logout.remoteProviderId)
return return_logout_error(request, logout,
AUTHENTIC_STATUS_CODE_UNAUTHORIZED), None, None
if not policy.accept_slo:
logger.warn('common_processing: received slo from %s not authorized'\
% logout.remoteProviderId)
return return_logout_error(request, logout,
AUTHENTIC_STATUS_CODE_UNAUTHORIZED), None, None
# Look for a session index
try:
session_index = logout.request.sessionIndex
except:
pass
fed = lookup_federation_by_name_identifier(profile=logout)
if not fed:
logger.warning('common_processing: unknown user for %s' \
% logout.request.dump())
return return_logout_error(request, logout,
lasso.SAML2_STATUS_CODE_UNKNOWN_PRINCIPAL), None, None
session = None
try:
session = LibertySessionSP. \
objects.get(federation=fed, session_index=session_index)
except LibertySessionSP.DoesNotExist:
logger.warning('common_processing: No Liberty session found')
return return_logout_error(request, logout,
lasso.SAML2_STATUS_CODE_UNKNOWN_PRINCIPAL), None, None
q = LibertySessionDump. \
objects.filter(django_session_key=session.django_session_key)
if not q:
logger.warning('common_processing: \
No session dump for this session')
return return_logout_error(request, logout,
lasso.SAML2_STATUS_CODE_UNKNOWN_PRINCIPAL), None, None
logger.info('common_processing: from %s, \
for session index %s and session %s' % \
(logout.remoteProviderId, session_index, session.id))
logout.setSessionFromDump(q[0].session_dump.encode('utf8'))
q.delete()
try:
logout.validateRequest()
except lasso.Error, error:
msg = 'common_processing: error validateRequest: %s' \
% lasso.strError(error[0])
logger.info(msg)
# We continue the process
return None, session, logout
@csrf_exempt
def singleLogoutSOAP(request):
'''
Single Logout IdP initiated by SOAP
'''
message = None
try:
message = get_soap_message(request)
except:
return http_response_bad_request('singleLogoutSOAP: Bad SOAP message')
if not message:
return http_response_bad_request('singleLogoutSOAP: Bad SOAP message')
request_type = lasso.getRequestTypeFromSoapMsg(message)
if request_type != lasso.REQUEST_TYPE_LOGOUT:
return http_response_bad_request('singleLogoutSOAP: \
SOAP message is not a slo message')
error, session, logout = \
common_processing_slo_idp_init_bindings(request, message)
if error:
return error
'''
Play the role of IdP sending a SLO to all SP
'''
slo_soap_as_idp(request, logout, session)
'''Break local session and respond to the IdP initiating the SLO'''
try:
flush_django_session(session.django_session_key)
session.delete()
except Exception, e:
logger.error('singleLogoutSOAP: Error at session deletion due to %s' \
% str(e))
return finishSingleLogoutSOAP(logout)
return finishSingleLogoutSOAP(logout)
def finishSingleLogoutSOAP(logout):
try:
logout.buildResponseMsg()
except lasso.Error, error:
message = 'singleLogoutSOAP \
buildResponseMsg: %s' % lasso.strError(error[0])
return http_response_forbidden_request(message)
django_response = HttpResponse()
django_response.status_code = 200
django_response.content_type = 'text/xml'
django_response.content = logout.msgBody
return django_response
def finish_slo(request):
id = request.REQUEST.get('id')
if not id:
logger.error('finish_slo: missing id argument')
return HttpResponseBadRequest('finish_slo: missing id argument')
logout_dump, session_key, provider_id = get_and_delete_key_values(id)
server = create_server(request)
logout = lasso.Logout.newFromDump(server, logout_dump)
load_provider(request, provider_id, server=logout.server,
sp_or_idp='idp')
#Break local session and respond to the IdP initiating the SLO
if logout.isSessionDirty:
if logout.session:
save_session(request, logout, session_key=session_key,
kind=LIBERTY_SESSION_DUMP_KIND_SP)
else:
delete_session(request, session_key=session_key)
remove_liberty_session_sp(request, session_key=session_key)
return slo_return_response(logout)
def singleLogout(request):
'''
Single Logout IdP initiated by Redirect
'''
message = get_saml2_query_request(request)
if not message:
return http_response_forbidden_request('singleLogout: \
Unable to handle Single Logout by Redirect without request')
error, session, logout = \
common_processing_slo_idp_init_bindings(request, message)
if error:
return error
if settings.IDP_SAML2:
save_key_values(logout.request.id, logout.dump(),
request.session.session_key, logout.remoteProviderId)
return idp_views.redirect_to_logout(request, next_page='%s?id=%s' %
(reverse(finish_slo), urllib.quote(logout.request.id)))
try:
flush_django_session(request.session.session_key)
session.delete()
except Exception, e:
logger.error('singleLogout: Error at session deletion due to %s' \
% str(e))
return slo_return_response(logout)
return slo_return_response(logout)
def slo_return_response(logout):
try:
logout.buildResponseMsg()
except lasso.Error, error:
return http_response_forbidden_request('slo_return_response: %s' \
% lasso.strError(error[0]))
else:
logger.info('slo_return_response: redirect to %s' % logout.msgUrl)
return HttpResponseRedirect(logout.msgUrl)
###
# federationTermination
# @request
# @method
# @entity_id
#
# Name Identifier Management
# Federation termination: request from user interface
# Profile supported: Redirect, SOAP
# For response, if the requester uses a (a)synchronous binding,
# the responder uses the same.
# Else, the grabs the preferred method from the metadata.
# By default we do not break the session.
# TODO: Define in admin a parameter to indicate if the
# federation termination implies a local logout (IDP and SP initiated)
# -> Should not logout.
# TODO: Clean tables of all dumps about this user
###
def federationTermination(request):
entity_id = request.REQUEST.get('entity_id')
if not entity_id:
return error_page(request,
_('fedTerm/SP UI: No provider for defederation'),
logger=logger)
if request.user.is_anonymous():
return error_page(request,
_('fedTerm/SP UI: Unable to defederate a not logged user!'),
logger=logger)
server = build_service_provider(request)
if not server:
error_page(request,
_('fedTerm/SP UI: Service provider not configured'),
logger=logger)
# Lookup for the Identity provider
p = load_provider(request, entity_id, server=server, sp_or_idp='idp')
if not p:
return error_page(request,
_('fedTerm/SP UI: No such identity provider.'),
logger=logger)
manage = lasso.NameIdManagement(server)
load_session(request, manage, kind=LIBERTY_SESSION_DUMP_KIND_SP)
load_federation(request, get_entity_id(request, reverse(metadata)), manage)
fed = lookup_federation_by_user(request.user, p.entity_id)
if not fed:
return error_page(request,
_('fedTerm/SP UI: Not a valid federation'),
logger=logger)
# The user asks a defederation,
# we perform without knowing if the IdP can handle
fed.delete()
# TODO: Deal with identity provider configuration in policies
# If not defined in the metadata,
# put ANY to let lasso do its job from metadata
if not p.identity_provider.enable_http_method_for_defederation_request:
try:
manage.initRequest(entity_id, None, lasso.HTTP_METHOD_ANY)
except lasso.Error, error:
return error_page(request,
_('fedTerm/SP UI: %s') % lasso.strError(error[0]),
logger=logger)
if manage.msgBody:
try:
manage.buildRequestMsg()
except lasso.Error, error:
return error_page(request,
_('fedTerm/SP SOAP: %s') % lasso.strError(error[0]),
logger=logger)
# TODO: Client cert
client_cert = None
try:
soap_answer = soap_call(manage.msgUrl,
manage.msgBody, client_cert=client_cert)
except SOAPException:
return error_page(request,
_('fedTerm/SP SOAP: \
Unable to perform SOAP defederation request'),
logger=logger)
return manage_name_id_return(request, manage, soap_answer)
else:
try:
manage.buildRequestMsg()
except lasso.Error, error:
return error_page(request,
_('fedTerm/SP Redirect: %s') % \
lasso.strError(error[0]), logger=logger)
save_manage(request, manage)
return HttpResponseRedirect(manage.msgUrl)
# Else, taken from config
if p.identity_provider.http_method_for_defederation_request == \
lasso.HTTP_METHOD_SOAP:
try:
manage.initRequest(entity_id, None, lasso.HTTP_METHOD_SOAP)
manage.buildRequestMsg()
except lasso.Error, error:
return error_page(request,
_('fedTerm/SP SOAP: %s') % lasso.strError(error[0]),
logger=logger)
# TODO: Client cert
client_cert = None
try:
soap_answer = soap_call(manage.msgUrl,
manage.msgBody, client_cert=client_cert)
except SOAPException:
return error_page(request,
_('fedTerm/SP SOAP: \
Unable to perform SOAP defederation request'),
logger=logger)
return manage_name_id_return(request, manage, soap_answer)
if p.identity_provider.http_method_for_defederation_request == \
lasso.HTTP_METHOD_REDIRECT:
try:
manage.initRequest(entity_id, None, lasso.HTTP_METHOD_REDIRECT)
manage.buildRequestMsg()
except lasso.Error, error:
return error_page(request,
_('fedTerm/SP Redirect: %s') % lasso.strError(error[0]),
logger=logger)
save_manage(request, manage)
return HttpResponseRedirect(manage.msgUrl)
return error_page(request, _('Unknown HTTP method.'), logger=logger)
###
# manageNameIdReturn
# @request
#
# Federation termination: response from Redirect SP initiated
###
def manageNameIdReturn(request):
server = build_service_provider(request)
if not server:
return error_page(request,
_('fedTerm/SP Redirect: Service provider not configured'),\
logger=logger)
manage_dump = get_manage_dump(request)
manage = None
if manage_dump.exists() and manage_dump.count() > 1:
manage_dump.delete()
return error_page(request,
_('fedTerm/SP Redirect: Error managing manage dump'),
logger=logger)
elif manage_dump.exists():
try:
manage = \
lasso.NameIdManagement.newFromDump(server,
manage_dump[0].manage_dump)
except:
pass
manage_dump.delete()
else:
manage = lasso.NameIdManagement(server)
if not manage:
return error_page(request,
_('fedTerm/SP Redirect: Defederation failed'), logger=logger)
load_federation(request, get_entity_id(request, reverse(metadata)), manage)
message = get_saml2_request_message(request)
return manage_name_id_return(request, manage, message)
###
# manage_name_id_return
# @request
# @logout
# @message
#
# Post-response processing
###
def manage_name_id_return(request, manage, message):
while True:
try:
manage.processResponseMsg(message)
except (lasso.ServerProviderNotFoundError,
lasso.ProfileUnknownProviderError):
provider_id = manage.remoteProviderId
provider_loaded = load_provider(request, provider_id,
server=manage.server, sp_or_idp='idp')
if not provider_loaded:
message = _('fedTerm/Return: \
provider %r unknown') % provider_id
return error_page(request, message, logger=logger)
else:
continue
except lasso.Error, error:
return error_page(request,
_('fedTerm/manage_name_id_return: %s') % \
lasso.strError(error[0]),
logger=logger)
return HttpResponseRedirect(get_registered_url(request))
###
# manageNameIdSOAP
# @request
#
# Federation termination: request from SOAP IdP initiated
# TODO: Manage valid soap responses on error (else error 500)
###
@csrf_exempt
def manageNameIdSOAP(request):
try:
soap_message = get_soap_message(request)
except:
return http_response_bad_request('fedTerm/IdP SOAP: Bad SOAP message')
if not soap_message:
return http_response_bad_request('fedTerm/IdP SOAP: Bad SOAP message')
request_type = lasso.getRequestTypeFromSoapMsg(soap_message)
if request_type != lasso.REQUEST_TYPE_NAME_ID_MANAGEMENT:
return http_response_bad_request('fedTerm/IdP SOAP: \
SOAP message is not a slo message')
server = build_service_provider(request)
if not server:
return http_response_forbidden_request('fedTerm/IdP SOAP: \
Service provider not configured')
manage = lasso.NameIdManagement(server)
if not manage:
return http_response_forbidden_request('fedTerm/IdP SOAP: \
Unable to create Login object')
while True:
try:
manage.processRequestMsg(soap_message)
break
except (lasso.ServerProviderNotFoundError,
lasso.ProfileUnknownProviderError):
provider_id = manage.remoteProviderId
provider_loaded = load_provider(request, provider_id,
server=server, sp_or_idp='idp')
if not provider_loaded:
message = _('fedTerm/SOAP: provider %r unknown') % provider_id
return error_page(request, message, logger=logger)
else:
continue
except lasso.Error, error:
message = 'fedTerm/IdP SOAP: %s' % lasso.strError(error[0])
return http_response_forbidden_request(message)
fed = lookup_federation_by_name_identifier(profile=manage)
load_federation(request, get_entity_id(request, reverse(metadata)), manage, fed.user)
try:
manage.validateRequest()
except lasso.Error, error:
message = 'fedTerm/IdP SOAP: %s' % lasso.strError(error[0])
return http_response_forbidden_request(message)
fed.delete()
try:
manage.buildResponseMsg()
except:
message = 'fedTerm/IdP SOAP: %s' % lasso.strError(error[0])
return http_response_forbidden_request(message)
django_response = HttpResponse()
django_response.status_code = 200
django_response.content_type = 'text/xml'
django_response.content = manage.msgBody
return django_response
###
# manageNameId
# @request
#
# Federation termination: request from Redirect IdP initiated
###
def manageNameId(request):
query = get_saml2_query_request(request)
if not query:
return http_response_forbidden_request('fedTerm/IdP Redirect: \
Unable to handle Single Logout by Redirect without request')
server = build_service_provider(request)
if not server:
return http_response_forbidden_request('fedTerm/IdP Redirect: \
Service provider not configured')
manage = lasso.NameIdManagement(server)
if not manage:
return http_response_forbidden_request('fedTerm/IdP Redirect: \
Unable to create Login object')
try:
manage.processRequestMsg(query)
except lasso.Error, error:
message = 'fedTerm/IdP Redirect: %s' % lasso.strError(error[0])
return http_response_forbidden_request(message)
fed = lookup_federation_by_name_identifier(profile=manage)
load_federation(request, get_entity_id(request, reverse(metadata)), manage, fed.user)
try:
manage.validateRequest()
except lasso.Error, error:
logger.warning('fedTerm/IdP Redirect: Unable to validate request')
return
fed.delete()
try:
manage.buildResponseMsg()
except:
message = 'fedTerm/IdP Redirect: %s' % lasso.strError(error[0])
return http_response_forbidden_request(message)
return HttpResponseRedirect(manage.msgUrl)
#############################################
# Helper functions
#############################################
def get_provider_id_and_options(provider_id):
if not provider_id:
provider_id = reverse(metadata)
options = metadata_options
if getattr(settings, 'AUTHSAML2_METADATA_OPTIONS', None):
options.update(settings.AUTHSAML2_METADATA_OPTIONS)
return provider_id, options
def get_metadata(request, provider_id=None):
provider_id, options = get_provider_id_and_options(provider_id)
return get_saml2_metadata(request, provider_id, sp_map=metadata_map,
options=options)
def create_server(request, provider_id=None):
provider_id, options = get_provider_id_and_options(provider_id)
return create_saml2_server(request, provider_id, sp_map=metadata_map,
options=options)
def http_response_bad_request(message):
logger.error(message)
return HttpResponseBadRequest(_(message))
def http_response_forbidden_request(message):
logger.error(message)
return HttpResponseForbidden(_(message))
def build_service_provider(request):
return create_server(request, reverse(metadata))
def setAuthnrequestOptions(provider, login, force_authn, is_passive):
if not provider or not login:
return None
p = get_idp_options_policy(provider)
if not p:
return None
if p.no_nameid_policy:
login.request.nameIDPolicy = None
else:
login.request.nameIDPolicy.format = \
NAME_ID_FORMATS[p.requested_name_id_format]['samlv2']
login.request.nameIDPolicy.allowCreate = p.allow_create
login.request.nameIDPolicy.spNameQualifier = None
if p.enable_binding_for_sso_response:
login.request.protocolBinding = p.binding_for_sso_response
if force_authn is None:
force_authn = p.want_force_authn_request
login.request.forceAuthn = force_authn
if is_passive is None:
is_passive = p.want_is_passive_authn_request
login.request.isPassive = is_passive
return p
def profile(request, template_name='profile.html'):
if 'next' in request.session:
next = request.session['next']
else:
next = next
if request.user is None \
or not request.user.is_authenticated() \
or not hasattr(request.user, '_meta'):
return HttpResponseRedirect(next)
logger.info('view_profile: View profile of user %s' % str(request.user))
#Add creation date
federations = LibertyProvider.objects \
.filter(identity_provider__libertyfederation__user=request.user) \
.values_list('name', flat=True)
from frontend import AuthSAML2Frontend
form = AuthSAML2Frontend().form()()
if not form.fields['provider_id'].choices:
form = None
context = {'submit_name': 'submit-%s' % AuthSAML2Frontend().id(),
REDIRECT_FIELD_NAME: '/profile',
'form': form}
return render_to_string(template_name,
{'next': next,
'federations': federations,
'base': '/authsaml2'},
RequestContext(request, context))
@login_required
@csrf_exempt
def delete_federation(request, next_url='/'):
next = request.REQUEST.get('next', next_url)
logger.info('delete_federation: federation deletion requested')
if request.method == "POST":
provider_name = request.POST.get('fed')
if provider_name:
LibertyFederation.objects.filter(
user=request.user,
idp__liberty_provider__name=provider_name).update(user=None)
logger.info('delete_federation: federation %s deleted',
provider_name)
messages.add_message(request, messages.INFO,
_('Successful federation deletion.'))
return HttpResponseRedirect(next)
return HttpResponseRedirect(next)