This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
glasnost/shared/web/IdentitiesWeb.py

1650 lines
69 KiB
Python

# -*- coding: UTF-8 -*-
# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
# Romain Chantereau <rchantereau@entrouvert.com>
# Nicolas Clapiès <nclapies@easter-eggs.org>
# Pierre-Antoine Dejace <padejace@entrouvert.be>
# Thierry Dulieu <tdulieu@easter-eggs.com>
# Florent Monnier <monnier@codelutin.com>
# Cédric Musso <cmusso@easter-eggs.org>
# Frédéric Péters <fpeters@entrouvert.be>
# Benjamin Poussin <poussin@codelutin.com>
# Emmanuel Raviart <eraviart@entrouvert.com>
# Sébastien Régnier <regnier@codelutin.com>
# Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
# Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
# Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
__doc__ = """Glasnost Identities Web"""
__version__ = '$Revision$'[11:-2]
import httplib
import md5
try:
from OpenSSL import SSL
except ImportError:
SSL = None
import socket
import urllib
import urlparse
import whrandom
from xml.dom.minidom import parseString
try:
import lasso.Protocols.SingleSignOnAndFederation as sso
import lasso.Protocols.NameRegistration as NameRegistration
import lasso.Protocols.SingleLogout as SingleLogout
import lasso.Protocols.FederationTerminationNotification as Defederation
import lasso.Schemas.SchemaDom as SchemaDom
import lasso.Soap as Soap
import lasso.Tools as lassoTools
import lasso.Constants as Constants
except ImportError:
sso = None
try:
import libxml2
except ImportError:
libxml2 = None
import glasnost.common.context as context
import glasnost.common.faults as faults
import glasnost.common.slots as slots
import glasnost.common.xhtmlgenerator as X
import glasnost.proxy.IdentitiesProxy as proxyIdentities
from glasnost.proxy.tools import getProxyForServerRole
import ObjectsWeb as objects
import things
from tools import *
class AdminIdentities(objects.AdminMixin, proxyIdentities.AdminIdentities):
pass
objects.register(AdminIdentities)
class HTTPSConnection(httplib.HTTPConnection):
certificateFile = None
default_port = httplib.HTTPS_PORT
peerCaCertificateFile = None
privateKeyFile = None
def __init__(self, host, port = None, privateKeyFile = None,
certificateFile = None, peerCaCertificateFile = None,
strict = None):
httplib.HTTPConnection.__init__(self, host, port, strict)
self.privateKeyFile = privateKeyFile
self.certificateFile = certificateFile
self.peerCaCertificateFile = peerCaCertificateFile
def connect(self):
"Connect to a host on a given (SSL) port."
context = SSL.Context(SSL.SSLv23_METHOD)
# Demand a certificate.
context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
self.verifyCallback)
context.use_privatekey_file(self.privateKeyFile)
context.use_certificate_file(self.certificateFile)
context.load_verify_locations(self.peerCaCertificateFile)
# Strange hack, that is derivated from httplib.HTTPSConnection, but
# that I (Emmanuel) don't really understand...
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sslSocket = SSL.Connection(context, sock)
sslSocket.connect((self.host, self.port))
self.sock = httplib.FakeSocket(sslSocket, sslSocket)
def verifyCallback(self, connection, x509Object, errorNumber, errorDepth,
returnCode):
# FIXME: What should be done?
return returnCode
class Identification(things.ThingMixin, proxyIdentities.Identification):
pass
things.register(Identification)
class Identity(objects.ObjectWebMixin, proxyIdentities.Identity):
pass
objects.register(Identity)
class IdentitiesWeb(objects.ObjectsWebMixin, proxyIdentities.IdentitiesProxy):
def createSessionIfNeeded(self, nextUri):
"""Create a session, if it doesn't exist yet.
Return either None (if the session already existed or has been created
immediately) or a redirect page which must be returned to the user
agent, in order to create the session.
"""
session = context.getVar('session')
if session is None:
req = context.getVar('req')
sessionsProxy = getProxyForServerRole('sessions')
virtualHost = context.getVar('virtualHost')
try:
session = sessionsProxy.newSession(
req.connection.remote_ip,
serverId = virtualHost.defaultDispatcherId)
except: # Do a tighter check?
if context.getVar('debug'):
raise
return failure(_('Failed to initialize a session.'),
X.roleUrl('login'))
sessionToken = session['sessionToken']
context.setVar('sessionToken', sessionToken)
context.setVar('session', session)
canUseCookie = context.getVar('canUseCookie', default = 0)
if canUseCookie:
url = X.roleUrl('define', action = 'testCookie')
url.add('nextUri', nextUri)
url.add('sessionToken', sessionToken)
context.setVar('canUseCookie', 0)
url = url.getAsUrl()
context.setVar('canUseCookie', 1)
return redirect(url)
return None
def deleteSession(self):
self.deleteUserToken()
session = context.getVar('session')
# get the host name to find the Name Identity entry
# of the current session :
if session is not None:
# Don't delete the session, just remove userToken from it.
context.setVar('userId', None)
context.setVar('user', None)
if session.has_key('userToken'):
del session['userToken']
session['isDirty'] = 1
context.setVar('userToken', '')
# if not context.getVar('sessionTokenInCookie', default = 0):
# # The sessionToken was not stored in a cookie, so don't try to
# # use
# # the cookie when loging out.
# context.setVar('canUseCookie', 0)
nextUri = context.getVar('nextUri') or ''
if not nextUri:
nextUri = '/'
else:
nextUri = cleanUpUri(nextUri, ['sessionToken'])
canUseCookie = context.getVar('canUseCookie', default = 0)
if not canUseCookie:
nextUri = appendToUri(nextUri,
'sessionToken=' + context.getVar('sessionToken'))
def federationTerminationRequestUrl(self):
# FIXME : process the federation termination notification :
session = context.getVar('session')
if not session:
return accessForbidden()
peerHostName = session['peerHostName']
# get the name identifier :
nameIdentifier = None
remoteProvider = self.isServiceProvider()
if remoteProvider:
nameIdentifier = self.getLocalNameIdentifierServiceProvider(
peerHostName)
else:
remoteProvider = self.isIdentityProvider(peerHostName)
nameIdentifier = self.getPeerNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
nameIdentifier = self.getLocalNameIdentifierIdentityProvider(
peerHostName)
handlerUrl = remoteProvider.federationTerminationServiceUrl
providerId = context.getVar('httpHostName')
# get the protocole profile :
protocolProfiles = remoteProvider.federationTerminationProtocolProfiles
if not protocolProfiles:
raise 'no protocol profile supported'
profile = protocolProfiles[0]
# HTTP with redirect 302 profile :
if profile == Constants.protocolProfiles['fedTermIdpHttp'] or \
profile == Constants.protocolProfiles['fedTermSpHttp']:
url = Defederation.getFederationTerminationNotificationHandlerUrl(
handlerUrl, providerId, nameIdentifier)
return redirect(url)
# HTTP SOAP profile :
elif profile == Constants.protocolProfiles['fedTermIdpSoap'] or \
profile == Constants.protocolProfiles['fedTermSpSoap']:
request = Defederation.buildFederationTerminationNotificationRequest(
providerId, nameIdentifier)
response = self.sendSoapRequest(request)
return OK
else:
raise 'no profile defined'
federationTerminationRequestUrl.isPublicForWeb = 1
def federationTerminationNotificationServiceReturnUrl(self, **keywords):
return redirect('http://localhost') # FIXME : where to redirect ?
federationTerminationNotificationServiceReturnUrl.isPublicForWeb = 1
def federationTerminationServiceUrl(self, **keywords):
# rebuild the request :
request = Defederation.buildFederationTerminationNotificationFromKeywords(
keywords)
# build the response :
response = self.processFederationTerminationNotification(request)
remoteProvider = self.isServiceProvider()
if not remoteProvider:
peerHostName = request.getProviderID()
remoteProvider = self.isIdentityProvider(peerHostName)
handlerUrl = remoteProvider.federationTerminationServiceReturnUrl
return redirect(handlerUrl)
federationTerminationServiceUrl.isPublicForWeb = 1
def getViewAllActionButtonsBarLayout(self):
layout = X.array()
if self.canAddObject():
layout += X.buttonStandalone(_('New account'),
X.actionUrl('newAccount'))
layout += X.buttonStandalone('new', X.actionUrl('edit'))
return layout
def getViewAboveButtonsBarLayout(self, object, fields):
ballotsWeb = getWebForServerRole('ballots')
electionsWeb = getWebForServerRole('elections')
layout = X.array()
layout += objects.ObjectsWebMixin.getViewAboveButtonsBarLayout(
self, object, fields)
def getVotesSectionLayout(person, votes, elections, intertitle):
layout = None
if len(elections) > 0:
layout = X.array( X.h3()(intertitle) )
layout += X.br()
table = X.table()
layout += table
for election in elections:
if not person.id in election.getVoterIds():
continue
tr = X.tr(X.th(scope = 'row')(
X.a(href = X.idUrl(election.id))(
election.getLabelTranslated(
context.getVar('readLanguages')))))
table += tr
if not votes.has_key(election.id):
tr += X.td(_('Abstention'))
else:
vote = votes[election.id]
if vote == 'secret':
tr += X.td(_('Secret Ballot'))
else:
tr += X.td(X.a(href = X.idUrl(vote.id))(
X.asIs(vote.getLabelLine(election))))
return layout
userId = context.getVar('userId')
knownRoles = context.getVar('knownRoles')
if userId and 'ballots' in knownRoles and 'elections' in knownRoles:
# Only an identified user can see someone's votes.
votes = ballotsWeb.getVotesFromTokens(object.voteTokens)
try:
lastElections = electionsWeb.getLastObjectsWithVoter(
50, object.id, ['running'])
except faults.UserAccessDenied:
lastElections = []
layout += getVotesSectionLayout(
object, votes, lastElections,
_("""The votes for the elections in progress"""))
try:
lastElections = electionsWeb.getLastObjectsWithVoter(
10, object.id, ['closed'])
except faults.UserAccessDenied:
lastElections = []
layout += getVotesSectionLayout(
object, votes, lastElections,
_("""The votes for the closed elections"""))
return layout
def isServiceProvider(self):
""" return None if is an identity provider, else return the remote
identity provider """
identityProviderId = None
providersWeb = getWebForServerRole('providers')
if providersWeb is not None:
try:
identityProviderId = providersWeb.getRemoteIdentityProviderId()
except faults.MissingItem:
pass
identityProvider = None
if identityProviderId:
identityProvider = providersWeb.getObject(identityProviderId)
return identityProvider
def isIdentityProvider(self, serviceProviderHostName):
""" return None if not an identity provider (serviceProviderHostName
is not a remote service provider), else return the remote service
provider """
providersWeb = getWebForServerRole('providers')
serviceProviderId = None
serviceProviderId = providersWeb.getServiceProviderId(
serviceProviderHostName)
serviceProvider = None
if serviceProviderId:
serviceProvider = providersWeb.getObject(serviceProviderId)
return serviceProvider
def login(self): # FIXME: Rename to signOn or authentication or ...,
providerId = context.getVar('httpHostName')
loginUrl = self.getSingleSignOnRequestUrl(
providerId)
return redirect(loginUrl)
login.isPublicForWeb = 1
def getSingleSignOnRequestUrl(self, providerId, nameIdPolicy = 'none'):
""" forge the url to redirect the user and negociate a sso """
# decide which type of authentication to do.
# (local authentication or liberty alliance authentication ) :
identityProviderId = None
providersWeb = getWebForServerRole('providers')
if providersWeb is not None:
try:
identityProviderId = providersWeb.getRemoteIdentityProviderId()
except faults.MissingItem:
pass
# Don't use Liberty Alliance protocol to authenticate user.
if identityProviderId is None:
return self.loginLocal(context.getVar('nextUri'))
identityProvider = providersWeb.getObject(identityProviderId)
# choose a protocole profile :
profile = 'artifact'
remoteProvider = self.isServiceProvider()
if remoteProvider:
protocolProfiles = remoteProvider.singleSignOnProtocolProfiles
if not protocolProfiles:
raise 'Missing SingleSignOnProtocolProfile in IDP metadata'
profile = lassoTools.getProfileKey(protocolProfiles[0])
# get and crypt the RelayState :
# FIXME : choose a different way to store the password for ciphering
relayState = 'http://' + context.getVar('httpHostName')
if 0: # FIXME: encryption doesn't work
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.encryptRelayState(relayState, symmetricKey)
# choose the authentication methods needed :
authenticationMethods = ['password']
# FIXME : implement the assertion consumer service id
# verify the Scheme of the single sign on service url (HTTPS) :
singleSignOnServiceUrl = identityProvider.singleSignOnServiceUrl
#if singleSignOnServiceUrl.startswith('http://'):
# raise 'HTTP for sso is not secure'
# forge the url and redirect :
ssoUrl = sso.getIdentityProviderAuthenticationUrl(
authenticationMethods = authenticationMethods,
profile = profile,
nameIdPolicy = nameIdPolicy,
serviceProviderId = providerId,
singleSignOnServiceUrl = singleSignOnServiceUrl)
return ssoUrl
def singleSignOnServiceUrl(self, **authenticationRequestKeywords):
"""Liberty Alliance Identity Provider Login Method."""
# init the nextUri :
nextUri = X.actionUrl('singleSignOnServiceUrl')
for key, value in authenticationRequestKeywords.items():
nextUri.add(key, value)
redirectPage = self.createSessionIfNeeded(nextUri)
if redirectPage is not None:
return redirectPage
# udpate the state of the session :
session = context.getVar('session')
session['authenticationRequestKeywords'] \
= authenticationRequestKeywords
session['isDirty'] = 1
# build the Authentication Request :
authenticationRequest = sso.buildAuthenticationRequestFromKeywords(
authenticationRequestKeywords)
# verify the providerId :
providerId = authenticationRequest.getProviderID()
# process the AssertionConsumerServiceID if exists :
# FIXME : add support of multi assertion consumer service urls.
serviceProvider = self.isIdentityProvider(providerId)
# FIXME : verifying the signature of the AuthnRequest if needed :
authnRequestSigned = serviceProvider.authnRequestSigned
if authnRequestSigned:
pass
# process the authentication method:
authenticationMethods = authenticationRequest.getAuthenticationMethods(
)
strongerMethods = ['smartcardPki', 'softwarePki', 'password']
chosenAuthenticationMethod = 'smartcardPki'
if len(authenticationMethods) > 0:
# choose the default authentication of the IDP :
authenticationComparison = \
authenticationRequest.getAuthnContextComparison()
if authenticationComparison == 'exact':
# choose the exactly requested method :
for method in authenticationMethods:
if method in strongerMethods:
chosenAuthenticationMethod = method
break
elif authenticationComparison == 'minimum':
# choose a method as strong as the one requested:
for method in strongerMethods:
if method in authenticationMethods:
chosenAuthenticationMethod = method
break
elif authenticationComparison == 'better':
# FIXME : ...
pass
# FIXME : define a way to know if the authentication process
# can be passive or not.
# process isPassive and forceAuthn :
isPassive, forceAuthentication = sso.processAuthenticationRequest(
authenticationRequest)
userId = context.getVar('userId')
if isPassive:
# do not interact with the principal :
if not userId:
url = sso.getServiceProviderAssertionArtifactHandlerUrl(
serviceProvider.assertionConsumerServiceUrl, # FIXME
sso.buildStatus('noPassive'))
return redirect(url)
return self.loginSucceeded2(session['authenticationMethod'],
authenticationRequest)
elif forceAuthentication or not userId:
# force authentication of the user even if already authenticated :
accountsServerRoles = {
'password': 'passwordaccounts',
'smartcardPki':'x509accounts',
'softwarePki': 'x509accounts',
'token': 'tokenaccounts',
}
accountsWeb = getWebForServerRole(
accountsServerRoles[chosenAuthenticationMethod]) # FIXME
if accountsWeb is None:
return failure(
_('Requested login method (%s) is not supported.') % (
chosenAuthenticationMethod), X.rootUrl('/'))
return accountsWeb.login()
else:
return self.loginSucceeded2(session['authenticationMethod'],
authenticationRequest)
singleSignOnServiceUrl.isPublicForWeb = 1
def loginLocal(self, authMeth = ''):
"""Non Liberty Alliance local login."""
# FIXME: this should be dynamic
authenticationMethods = ['password', 'softwarePki', 'token']
accountsServerRoles = {
'password': 'passwordaccounts',
'softwarePki': 'x509accounts',
'token': 'tokenaccounts',
}
if not authMeth or authMeth not in accountsServerRoles.keys():
authMeth = authenticationMethods[0]
accountsWeb = getWebForServerRole(accountsServerRoles[authMeth])
if not accountsWeb:
if context.getVar('debug'):
raise 'accountsWeb is None'
return pageNotFound()
return accountsWeb.login()
loginLocal.isPublicForWeb = 1
def loginSucceeded(self, userToken, authenticationMethod):
"""Liberty Alliance Identity Provider Login Succeeded Method."""
session = context.getVar('session')
newSession = 0
if not session:
newSession = 1
req = context.getVar('req')
sessionsProxy = getProxyForServerRole('sessions')
virtualHost = context.getVar('virtualHost')
try:
session = sessionsProxy.newSession(
req.connection.remote_ip,
serverId = virtualHost.defaultDispatcherId)
except: # Do a tighter check?
if context.getVar('debug'):
raise
return failure(_('Failed to initialize a session.'),
X.roleUrl('login'))
sessionToken = session['sessionToken']
context.setVar('session', session)
context.setVar('sessionToken', sessionToken)
session['authenticationMethod'] = authenticationMethod
context.setVar('userToken', userToken)
userId = self.getUserId()
user = None
if userId:
try:
user = self.getObject(userId)
except faults.MissingItem:
pass
if user is None:
userToken = ''
userId = None
if session.has_key('userToken'):
del session['userToken']
else:
session['userToken'] = userToken
session['isDirty'] = 1
context.setVar('userToken', userToken)
context.setVar('userId', userId)
context.setVar('user', user)
if not session.has_key('authenticationRequestKeywords'):
# Non Liberty Alliance local login.
if context.getVar('nextUri'):
nextUri = context.getVar('nextUri')
else:
nextUri = X.rootUrl()
if newSession and context.getVar('canUseCookie'):
url = X.roleUrl('define', action = 'testCookie')
url.add('nextUri', nextUri)
url.add('sessionToken', sessionToken)
context.setVar('canUseCookie', 0)
url = url.getAsUrl()
context.setVar('canUseCookie', 1)
return redirect(url)
else:
return redirect(nextUri)
# Liberty Alliance login.
authenticationRequestKeywords \
= session['authenticationRequestKeywords']
authenticationRequest = sso.buildAuthenticationRequestFromKeywords(
authenticationRequestKeywords)
return self.loginSucceeded2(authenticationMethod,
authenticationRequest)
def loginSucceeded2(self, authenticationMethod, authenticationRequest):
# function to return an AuthnResponse without an Assertion :
def authenticationResponseWithoutAssertion(
assertionConsumerServiceUrl,
statusKey,
authnRequest):
authnResponse = sso.buildAuthenticationResponseFromAuthnRequest(
context.getVar('httpHostName'),
statusKey,
None,
authnRequest)
url = '%s?%s' % (assertionConsumerServiceUrl,
authnResponse.exportToEncodedUrlEmbeddedMessage())
return redirect(url)
# check if a service identification exists for the user :
user = context.getVar('user')
serviceProviderHostName = authenticationRequest.getProviderID()
providersWeb = getWebForServerRole('providers')
try:
serviceProviderId = providersWeb.getServiceProviderId(
serviceProviderHostName)
serviceProvider = providersWeb.getObject(serviceProviderId)
except faults.MissingItem:
session = context.getVar('session')
session['isDirty'] = 1
del session['authenticationRequestKeywords']
return failure(_('Unknown service provider'))
serviceIdentification = None
if user.serviceIdentifications is not None:
for service in user.serviceIdentifications:
if service.peerHostName \
== serviceProviderHostName:
serviceIdentification = service
break
# no service identification found for the user, create an identity.
if serviceIdentification is None:
serviceIdentification = Identification()
serviceIdentification.peerHostName = serviceProviderHostName
digest = md5.new(serviceIdentification.peerHostName)
randomGenerator = whrandom.whrandom()
randomSalt = str(randomGenerator.uniform(0.1, 1))[2:]
digest.update(randomSalt)
serviceIdentification.localNameIdentifier = digest.hexdigest()
if user.serviceIdentifications is None:
user.serviceIdentifications = []
user.serviceIdentifications.append(serviceIdentification)
self.modifyPartialObject(user, ['serviceIdentifications'])
# process the NameIDPolicy :
statusCode = 'Success'
peerHostName = serviceProviderHostName
nameIdentifierPolicy = authenticationRequest.getNameIDPolicy()
nameIdentifier = None
# no federation, just return the peer/local name identifier :
if nameIdentifierPolicy == 'none':
nameIdentifier = self.getPeerNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
nameIdentifier = self.getLocalNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
return authenticationResponseWithoutAssertion(
serviceProvider.assertionConsumerServiceUrl,
'FederationDoesNotExist', authenticationRequest)
# do one time federation :
elif nameIdentifierPolicy == 'onetime':
nameIdentifier = lassoTools.generateNameIdentifier(peerHostName)
self.setLocalNameIdentifierIdentityProvider(
peerHostName, nameIdentifier)
# do a federation, return peer/local/new name identifier
elif nameIdentifierPolicy == 'federated':
nameIdentifier = self.getPeerNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
nameIdentifier = self.getLocalNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
nameIdentifier = lassoTools.generateNameIdentifier(
peerHostName)
self.setLocalNameIdentifierIdentityProvider(
peerHostName, nameIdentifier)
# federated else a none :
elif nameIdentifierPolicy == 'any':
consent = authenticationRequest.getConsent()
if consent:
nameIdentifier = self.getPeerNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
nameIdentifier = self.getLocalNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
nameIdentifier = lassoTools.generateNameIdentifier(
peerHostName)
self.setLocalNameIdentifierIdentityProvider(
peerHostName, nameIdentifier)
else:
return failure(_('No consent given by the principal'))
# get the format of the NameIdentifiers :
nameIdentifierFormat = 'federated'
idpNameIdentifierFormat = 'federated'
if nameIdentifierPolicy == 'onetime':
nameIdentifierFormat = 'onetime'
idpNameIdentifierFormat = 'onetime'
# build the assertion :
peerNameIdentifier = nameIdentifier
assertion = sso.buildAuthenticationAssertion(
authnRequest = authenticationRequest,
issuer = context.getVar('httpHostName'),
authenticationMethod = authenticationMethod,
nameIdentifier = peerNameIdentifier,
nameQualifier = peerHostName,
nameIdentifierFormat = nameIdentifierFormat,
idpNameIdentifier = serviceIdentification.localNameIdentifier,
idpNameQualifier = peerHostName,
idpNameIdentifierFormat = idpNameIdentifierFormat)
# save the peerHostName :
session = context.getVar('session')
if session:
session['peerHostName'] = serviceProviderHostName
# respond to the Service Provider :
profile = authenticationRequest.getProtocolProfile()
if profile == 'artifact':
# send an artifact, keep the assertion to be dereferenced :
assertionsProxy = getProxyForServerRole('assertions')
artifact = assertionsProxy.addAssertion(assertion.exportToString())
url = sso.getServiceProviderAssertionArtifactHandlerUrl(
serviceProvider.assertionConsumerServiceUrl, # FIXME
artifact,
authenticationRequest.getRelayState())
return redirect(url)
elif profile == 'post':
# send an AuthnResponse
authenticationResponse \
= sso.buildAuthenticationResponseFromAuthnRequest(
providerId = context.getVar('httpHostName'),
statusCode = 'Success',
assertion = assertion,
authnRequest = authenticationRequest)
authenticationResponseEmbedded \
= authenticationResponse.exportToEncodedUrlEmbeddedMessage()
# process with the POST format :
context.push(_level = 'loginSucceeded', layoutMode = 'edit')
try:
layout = X.array()
submitUrl = serviceProvider.assertionConsumerServiceUrl # FIXME
form = X.form(action = submitUrl,
enctype = 'multipart/form-data', method = 'post')
layout += form
form += X.input(name = 'LARES', type = 'hidden',
value = authenticationResponseEmbedded)
form += X.p(
_('You have been succesfully authenticated; click ok '\
'to go back to the service provider.'))
buttonsBar = X.div(_class = 'buttons-bar')
form += buttonsBar
buttonsBar += X.buttonInForm('ok', 'okButton')
return writePageLayout(layout, _('Authentication Succeeded'))
finally:
context.pull(_level = 'loginSucceeded')
else:
# unknown profile, dont know what to do :
raise 'Unknow protocol profile'
def logout(self):
self.deleteUserToken()
session = context.getVar('session')
if webTools.getConfig('DeleteSessionOnLogout'):
if session:
try:
getProxyForServerRole('sessions').deleteSession(
context.getVar('sessionToken'))
except UnknownSessionToken:
# uh ?
pass
nextUri = context.getVar('nextUri') or ''
if not nextUri:
nextUri = '/'
else:
nextUri = cleanUpUri(nextUri, ['sessionToken'])
return redirect(nextUri)
# Don't delete the session, just remove userToken from it.
if session:
context.setVar('userId', None)
context.setVar('user', None)
if session.has_key('userToken'):
del session['userToken']
session['isDirty'] = 1
context.setVar('userToken', '')
# if not context.getVar('sessionTokenInCookie', default = 0):
# # The sessionToken was not stored in a cookie, so don't try to
# # use
# # the cookie when loging out.
# context.setVar('canUseCookie', 0)
nextUri = context.getVar('nextUri') or ''
if not nextUri:
nextUri = '/'
else:
nextUri = cleanUpUri(nextUri, ['sessionToken'])
canUseCookie = context.getVar('canUseCookie', default = 0)
if not canUseCookie:
nextUri = appendToUri(nextUri,
'sessionToken=' + context.getVar('sessionToken'))
return redirect(nextUri)
logout.isPublicForWeb = 1
def newAccountNeededObjects(self):
roles = self.getRoles()
peopleWeb = getWebForServerRole(roles[0])
if not peopleWeb.canAddObject():
return accessForbidden()
person = peopleWeb.newObject()
knownRoles = context.getVar('knownRoles')
accountRole = 'passwordaccounts'
if 'passwordaccounts' in knownRoles and \
'x509accounts' in knownRoles:
# user should now decide account type
pass
accountsWeb = getWebForServerRole(accountRole)
if not accountsWeb.canAddObject():
return accessForbidden()
account = accountsWeb.newObject()
return (person, account)
def newAccount(self):
person, account = self.newAccountNeededObjects()
person.fillWithDefaultValues()
return self.newAccountObjects(person, account)
newAccount.isPublicForWeb = 1
def newAccountObjects(self, person, account):
context.push(_level = 'newAccountObjects', layoutMode = 'edit')
try:
layout = X.array()
if context.getVar('error'):
layout += person.getErrorLayout()
# or account.getErrorLayout() ?
form = X.form(action = X.actionUrl('newAccountSubmit'),
enctype = 'multipart/form-data', method = 'post')
layout += form
if context.getVar('nextUri'):
form += X.input(name = 'nextUri', type = 'hidden',
value = context.getVar('nextUri'))
slot = slots.Root(account, name = 'account')
identitySlot = account.getSlot('identityId')
identitySlot.getKind().stateInEditMode = 'hidden'
widget = slot.getWidget()
form += widget.getModelPageBodyLayout(slot, fields = None)
slot = slots.Root(person, name = 'person')
widget = slot.getWidget()
if person.id == None:
form += widget.getModelPageBodyLayout(slot, fields = None)
else:
personSlot = person.getSlot('id', parentSlot = slot)
widget = personSlot.getWidget()
form += widget.getModelHiddenLayout(personSlot, fields = None)
form += person.getViewLayout(None, parentSlot = slot)
form += X.div(_class = 'buttons-bar')(
X.span(_class = 'action-buttons-bar')(
X.buttonInForm('create', 'createButton')))
return writePageLayout(layout, _('New Account'))
finally:
context.pull(_level = 'newAccountObjects')
def newAccountSubmit(self, **keywords):
person, account = self.newAccountNeededObjects()
peopleWeb = person.getWeb()
accountsWeb = account.getWeb()
slot = slots.Root(account, name = 'account')
identitySlot = account.getSlot('identityId')
identitySlot.getKind().isRequired = 0
account.submitFields(keywords, parentSlot = slot)
slot = slots.Root(person, name = 'person')
personIdSlot = person.getSlot('id', parentSlot = slot)
person.id = personIdSlot.getWidget().submit(personIdSlot, keywords)
if person.id:
person = peopleWeb.getObject(person.id)
else:
person.submitFields(keywords, parentSlot = slot)
if context.getVar('again'):
return self.newAccountObjects(person, account)
if not account.identityId:
identity = self.newObject()
try:
identity.personId = peopleWeb.addObject(person)
person.id = identity.personId
except: # ?
return self.newAccountObjects(person, account)
account.identityId = self.addObject(identity)
try:
accountsWeb.addObject(account)
except faults.DuplicateLogin, f:
account.setError('self.login', f)
context.setVar('again', 1)
context.setVar('error', 1)
return self.newAccountObjects(person, account)
except faults.DuplicateSerial, f:
context.setVar('again', 1)
context.setVar('error', 1)
account.setError('self.serial', f)
return self.newAccountObjects(person, account)
if context.getVar('nextUri'):
return redirect(context.getVar('nextUri'))
return redirect(X.rootUrl())
newAccountSubmit.isPublicForWeb = 1
def newPerson(self):
# used when the user comes back from idp for the first time
peopleWeb = getWebForServerRole('people')
if not peopleWeb:
return redirect(X.rootUrl())
if not peopleWeb.canAddObject():
return accessForbidden()
person = peopleWeb.newObject()
return self.newPersonObject(person)
newPerson.isPublicForWeb = 0 # not a public url!
def newPersonObject(self, object):
context.push(_level = 'newPersonObject', layoutMode = 'edit')
try:
layout = X.array()
if context.getVar('error'):
layout += object.getErrorLayout()
form = X.form(action = X.actionUrl('newPersonSubmit'),
enctype = 'multipart/form-data', method = 'post')
layout += form
if context.getVar('nextUri'):
form += X.input(name = 'nextUri', type = 'hidden',
value = context.getVar('nextUri'))
slot = slots.Root(object)
widget = slot.getWidget()
form += widget.getModelPageBodyLayout(slot, fields = None)
form += X.div(_class = 'buttons-bar')(
X.span(_class = 'action-buttons-bar')(
X.buttonInForm('create', 'createButton')))
return writePageLayout(layout, _('New Account'))
finally:
context.pull(_level = 'newPersonObject')
def newPersonSubmit(self, **keywords):
if keywords is None:
keywords = {}
peopleWeb = getWebForServerRole('people')
person = peopleWeb.newObject(keywords)
person.submitFields(keywords)
if context.getVar('again'):
return self.newPersonObject(person)
try:
result = peopleWeb.submitAddObject(person)
except:
if context.getVar('debug'):
raise
return accessForbidden() # TODO: return failure ?
if result:
return result
user = context.getVar('user')
if user is not None:
user.personId = person.id
self.modifyPartialObject(user, ['personId'])
if context.getVar('nextUri'):
return redirect(context.getVar('nextUri'))
return redirect(X.idUrl(person.id))
newPersonSubmit.isPublicForWeb = 1
def processAuthenticationResponse(self, **keywords):
# Create the service provider side session.
nextUri = X.actionUrl('processAuthenticationResponse')
for key, value in keywords.items():
nextUri.add(key, value)
# create a session if needed :
redirectPage = self.createSessionIfNeeded(nextUri)
if redirectPage is not None:
return redirectPage
relayState = None
# process the response :
if keywords.has_key('LARES'):
# POST profile :
# get keyword from LARES dict :
authenticationResponseEmbedded = keywords['LARES']
# Remove base64 url encoding.
import lasso.Schemas.Schema as schema
authenticationResponseEmbeddedDecoded = \
schema.decodeEncodedUrlEmbeddedMessage(
authenticationResponseEmbedded)
# build the keyword :
authenticationResponseKeywords = {}
for segment in authenticationResponseEmbeddedDecoded.split('&'):
key, value = segment.split('=')
authenticationResponseKeywords[key] = value
# build the response from keywords :
response = sso.buildAuthenticationResponseFromKeywords(
authenticationResponseKeywords)
# decrypt the relay state :
relayState = response.getRelayState()
if 0: # FIXME: was if relayState: but encryption doesn't work
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.decryptRelayState(
relayState, symmetricKey)
# get the host name of the provider (IDP) :
identityProviderHostName = response.getProviderID()
elif keywords.has_key('SAMLArt'):
# Artifact profile :
if keywords.has_key('RelayState'):
relayState = keywords['RelayState']
if 0: # FIXME: encryption doesn't work
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.decryptRelayState(
relayState, symmetricKey)
del keywords['RelayState']
# build assertion artifact (in fact only get the value from
# SAMArt key ) :
assertionArtifact = sso.buildAssertionArtifactFromKeywords(
keywords)
# build the request, send it ,and get the response (the assertion)
# FIXME : get the providerId from the succintId if artifact
request = sso.buildRequest(assertionArtifact)
response = self.sendSoapRequest(request)
else:
# error, unknown profile :
response = sso.buildAuthenticationResponseFromKeywords(keywords)
relayState = response.getRelayState()
if 0: # was relayState: FIXME: encryption doesn't work
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.decryptRelayState(
relayState, symmetricKey)
# build the response :
statusCode = response.getStatusCode()
# error, stop now ! :
if statusCode != 'Success':
layout = X.array()
layout += X.p(_class = 'alert')(
_('Liberty Alliance authentication assertion request '
'failed (reason = %s)') % statusCode)
return writePageLayout(layout, _('Failure'), canCache = 0)
# get the assertion :
assertion = response.getAssertion()
# get peer name identifier (idp name identifier)
# local name identifier (sp name identifier if exists)
idpNameIdentifier = assertion.getIDPProvidedNameIdentifier()
nameIdentifier = assertion.getNameIdentifier()
# get the token of the user from the name identifiers.
# if no user token, no identities, so create one (user profile).
providersWeb = getWebForServerRole('providers')
identityProviderId = providersWeb.getRemoteIdentityProviderId()
identityProvider = providersWeb.getObject(identityProviderId)
identityProviderHostName = identityProvider.providerId
identityProvider = self.isServiceProvider()
if nameIdentifier and nameIdentifier != idpNameIdentifier:
try:
userToken = self.checkIdentityLocalNameIdentifierSP(
identityProviderHostName, nameIdentifier)
except faults.WrongNameIdentifier:
userToken = None
else:
try:
userToken = self.checkIdentityPeerNameIdentifierSP(
identityProviderHostName, idpNameIdentifier)
except faults.WrongNameIdentifier:
userToken = None
# get the RelayState, set the nextUri
nextUri = relayState
if nextUri:
canUseCookie = context.getVar('canUseCookie', default = 0)
if not canUseCookie:
sessionToken = context.getVar('sessionToken')
nextUri = appendToUri(nextUri,
'sessionToken=' + sessionToken)
else:
nextUri = X.rootUrl()
# set the session :
session = context.getVar('session')
session['peerHostName'] = identityProviderHostName
session['authenticationMethod'] = assertion.getAuthenticationMethod()
session['isDirty'] = 1
# if no token, set a new profile for the user :
if userToken is None and self.canAddObject():
user = Identity()
user.language = context.getVar('readLanguages')[0]
identification = Identification()
if nameIdentifier and nameIdentifier != idpNameIdentifier:
identification.localNameIdentifier = nameIdentifier
identification.peerHostName = identityProviderHostName
identification.peerNameIdentifier = idpNameIdentifier
user.identityIdentifications = [identification]
userId = self.addObject(user)
if nameIdentifier and nameIdentifier != idpNameIdentifier:
userToken = self.checkIdentityLocalNameIdentifierSP(
identityProviderHostName, nameIdentifier)
else:
userToken = self.checkIdentityPeerNameIdentifierSP(
identityProviderHostName, idpNameIdentifier)
session['userToken'] = userToken
context.setVar('userToken', userToken)
context.setVar('userId', userId)
context.setVar('user', user)
context.push(_level = 'processAuthenticationResponse',
nextUri = nextUri)
try:
return self.newPerson()
finally:
context.pull(_level = 'processAuthenticationResponse')
# complete the setting of the session :
context.setVar('userToken', userToken)
userId = self.getUserId()
user = None
if userId:
try:
user = self.getObject(userId)
except faults.MissingItem:
pass
if user is None:
userToken = ''
userId = None
if session.has_key('userToken'):
del session['userToken']
else:
session['userToken'] = userToken
session['isDirty'] = 1
context.setVar('userToken', userToken)
context.setVar('userId', userId)
context.setVar('user', user)
session = context.getVar('session')
session['peerHostName'] = identityProviderHostName
session['isDirty'] = 1
return redirect(nextUri)
processAuthenticationResponse.isPublicForWeb = 1
def processFederationTerminationNotification(self, request):
# FIXME : process request object
nameIdentifier = request.getNameIdentifier()
peerHostName = request.getProviderID()
remoteProvider = self.isServiceProvider()
if remoteProvider:
# the idp is stating the sp that it will no longer send assertions
#raise 'idp notify the sp it will no longer send assertions'
pass
else:
# the sp isstating the idp that it will no longer accept assertions
remoteProvider = self.isIdentityProvider(peerHostName)
#raise 'sp notify the idp it will not accept assertions'
response = HTTP_NO_CONTENT
return response
def processRegisterNameIdentifierRequest(self, request):
# validate the signature if present :
# Process a register name identifier registration request :
idpProvidedNameIdentifier = request.IDPProvidedNameIdentifier[0].PCDATA
spProvidedNameIdentifier = request.SPProvidedNameIdentifier[0].PCDATA
oldProvidedNameIdentifier = request.OldProvidedNameIdentifier[0].PCDATA
statusCode = Constants.statusCodes['Success']
peerHostName = request.getProviderID()
remoteProvider = self.isServiceProvider()
if remoteProvider:
# find the peer name identifier of the provider :
peerNameIdentifier = idpProvidedNameIdentifier
res = self.setPeerNameIdentifierServiceProvider(
peerHostName, peerNameIdentifier, oldProvidedNameIdentifier)
if not res:
statusCode = Constants.statusCodes['FederationDoesNotExist']
else:
remoteProvider = self.isIdentityProvider(peerHostName)
peerNameIdentifier = spProvidedNameIdentifier
res = self.setPeerNameIdentifierIdentityProvider(
peerHostName,
peerNameIdentifier,
oldProvidedNameIdentifier, idpProvidedNameIdentifier)
if not res:
statusCode = Constants.statusCodes['FederationDoesNotExist']
providerId = context.getVar('httpHostName')
relayState = request.getRelayState()
response = NameRegistration.buildRegisterNameIdentifierResponse(
providerId, statusCode, relayState = relayState)
return response
def processRequest(self, request):
# Process a single sign on request :
artifact = request.samlp_AssertionArtifact[0].PCDATA
assertionsProxy = getProxyForServerRole('assertions')
try:
assertionXml = assertionsProxy.getAssertion(artifact)
except faults.WrongArtifact:
# FIXME: Is it the proper way ton signal an error?
# I believe the good solution is to call buildResponse
# without any
# assertion => buildStatus should be modified.
response = sso.buildStatus('RequestDenied') # FIXME
except:
# FIXME: Is it the proper way ton signal an error?
# I believe the good solution is to call buildResponse
# without any
# assertion => buildStatus should be modified.
response = sso.buildStatus('RequestDenied') # FIXME
else:
assertionDom = libxml2.parseMemory(assertionXml, len(assertionXml))
assertion = SchemaDom.importFromNode(assertionDom, 1)
response = sso.buildResponse('Success', assertion)
return response
def processSingleLogoutRequest(self, request):
# FIXME : propagation to other idp
# FIXME : support of SessionIndex
peerHostName = request.getProviderID()
listIdentificationsToLogout = []
if not self.isServiceProvider():
# get the federation of the SP except its and send a logout :
user = context.getVar('user')
if user.serviceIdentifications is not None:
for identification in user.serviceIdentifications:
if identification.peerHostName != peerHostName:
listIdentificationsToLogout.append(identification)
# propagate the logout to the other provider :
# FIXME : only SOAP supported, because it's the easiest way
for identification in listIdentificationsToLogout:
nameIdentifier = identification.peerNameIdentifier
if not nameIdentifier:
nameIdentifier = identification.localNameIdentifier
logoutRequest = SingleLogout.buildSingleLogoutRequest(
context.getVar('httpHostName'), nameIdentifier)
response = self.sendSoapRequest(
logoutRequest, identification.peerHostName)
else:
nameIdentifier = request.getNameIdentifier()
self.deleteSession()
providerId = context.getVar('httpHostName')
statusCode = Constants.statusCodes['Success']
relayState = request.getRelayState()
response = SingleLogout.buildLogoutResponse(providerId,
statusCode, relayState = relayState)
return response
def registerNameIdentifierRequestUrl(self):
# FIXME : IDP must request a name registration through another profile
# test if a effective session exists :
session = context.getVar('session')
if session is None:
return accessForbidden()
# session is ok, so process the name identifiers :
peerHostName = session['peerHostName']
remoteProvider = self.isServiceProvider()
if remoteProvider:
# at service provider, change his name identifier :
idpProvidedNameIdentifier = self.getPeerNameIdentifierServiceProvider(peerHostName)
oldProvidedNameIdentifier = self.getLocalNameIdentifierServiceProvider(peerHostName)
if oldProvidedNameIdentifier == 0:
oldProvidedNameIdentifier = idpProvidedNameIdentifier
spProvidedNameIdentifier = lassoTools.generateNameIdentifier(
peerHostName)
self.setLocalNameIdentifierServiceProvider(
peerHostName, spProvidedNameIdentifier)
else:
# at identity provider, changer his name identifier :
remoteProvider = self.isIdentityProvider(peerHostName)
spProvidedNameIdentifier = self.getPeerNameIdentifierIdentityProvider(
peerHostName)
oldProvidedNameIdentifier = self.getLocalNameIdentifierIdentityProvider(
peerHostName)
idpProvidedNameIdentifier = lassoTools.generateNameIdentifier(
peerHostName)
self.setLocalNameIdentifierIdentityProvider(peerHostName,
idpProvidedNameIdentifier)
serviceUrl = remoteProvider.registerNameIdentifierServiceUrl
# get a profile :
profiles = remoteProvider.registerNameIdentifierProtocolProfiles
if not profiles:
raise 'no profile found at the provider'
protocolProfile = profiles[0] # FIXME : choose in local profiles too.
# get the provider identifier :
providerId = context.getVar('httpHostName')
# build the relay state : (FIXME: should not hardcode http://)
relayState = context.getVar('nextUri') or 'http://' + providerId
if not relayState:
relayState = context.getVar('httpHostName')
if relayState:
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.encryptRelayState(relayState, symmetricKey)
# send the request depending on the found profile :
if protocolProfile == Constants.protocolProfiles['rniIdpHttp'] or \
protocolProfile == Constants.protocolProfiles['rniSpHttp']:
# test if the service url is HTTPS :
if not serviceUrl.startswith('https://'):
return accessforbidden
# HTTP with redirect 302 profile :
url = NameRegistration.getRegisterNameIdentifierHandlerUrl(
serviceUrl, providerId, idpProvidedNameIdentifier,
spProvidedNameIdentifier, oldProvidedNameIdentifier,
relayState = relayState)
return redirect(url)
elif protocolProfile == Constants.protocolProfiles['rniIdpSoap'] or \
protocolProfile == Constants.protocolProfiles['rniSpSoap']:
# HTTP SOAP profile :
request = NameRegistration.buildRegisterNameIdentifierRequest(
providerId, idpProvidedNameIdentifier,
spProvidedNameIdentifier, oldProvidedNameIdentifier,
relayState)
response = self.sendSoapRequest(request)
if response.getStatusCode() == Constants['Success']:
relayState = response.getRelayState()
if relayState:
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.decrypt(relayState, symmetricKey)
return redirect(nextUri)
raise response.exportToString()
return accessForbidden() # FIXME : return 500 or another code ?
registerNameIdentifierRequestUrl.isPublicForWeb = 1
def registerNameIdentifierServiceReturnUrl(self, **keywords):
response = NameRegistration.buildRegisterNameIdentifierResponseFromKeywords(
keywords)
relayState = response.getRelayState()
if relayState:
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.decryptRelayState(relayState, symmetricKey)
return redirect(relayState)
else:
return OK
registerNameIdentifierServiceReturnUrl.isPublicForWeb = 1
def registerNameIdentifierServiceUrl(self, **keywords):
session = context.getVar('session')
peerHostName = session['peerHostName']
# process the request :
request = NameRegisration.buildRegisterNameIdentifierFromKeywords(
keywords)
response = self.processRegisterNameIdentifierRequest(request)
# return a response, get the provider handler url :
remoteProvider = self.isServiceProvider()
if not remoteProvider:
remoteProvider = self.isIdentityProvider(peerHostName)
handlerUrl = remoteProvider.registerNameIdentifierServiceReturnUrl
providerId = context.getVar('httpHostName')
url = NameRegistration.getRegisterNameIdentifierResponseHandlerUrl(
handlerUrl, providerId, response)
return redirect(url)
registerNameIdentifierServiceUrl.isPublicForWeb = 1
def sendSoapRequest(self, request, remoteProviderId = None):
""" remoteProviderId is used to send the request to a specific
provider """
providersWeb = getWebForServerRole('providers')
if self.isServiceProvider():
identityProviderId = providersWeb.getRemoteIdentityProviderId()
else:
if not remoteProviderId:
session = context.getVar('session')
peerHostName = session['peerHostName']
else:
peerHostName = remoteProviderId
identityProviderId = providersWeb.getServiceProviderId(
peerHostName)
identityProvider = providersWeb.getObject(identityProviderId)
identityProviderHostName = identityProvider.providerId
idpParsedSoapEndpoint = urlparse.urlparse(
identityProvider.soapEndpoint)
idpAddressingScheme, idpHostName, idpPath = idpParsedSoapEndpoint[:3]
requestSoapMessage = Soap.buildMessage(request.exportToString())
requestSoapMessage = urllib.quote_plus(requestSoapMessage, '=&')
headers = {'Content-type': 'text/xml'}
if idpAddressingScheme == 'https':
# FIXME : only allow secure http
# FIXME : Use PEM stored in glasnost attributes.
import tempfile, os
# private key :
identitiesWeb = getWebForServerRole('identities')
privateFd, privateKeyFile = tempfile.mkstemp()
os.write(privateFd, identitiesWeb.getPrivateKeySSL())
# cert and ca :
remoteProvider = self.isServiceProvider()
if not remoteProvider:
remoteProvider = self.isIdentityProvider(peerHostName)
certFd, certificateFile = tempfile.mkstemp()
os.write(certFd, remoteProvider.certificateSSL)
peerFd, peerCaCertificateFile = tempfile.mkstemp()
os.write(peerFd, remoteProvider.caCertificateSSL)
shortHostName = context.getVar('httpHostName').split('.')[0]
connection = HTTPSConnection(
idpHostName, privateKeyFile, certificateFile,
peerCaCertificateFile)
else:
# Use HTTP protocol.
connection = httplib.HTTPConnection(idpHostName)
connection.request('POST', idpPath, requestSoapMessage, headers)
responseSoapMessageFile = connection.getresponse()
responseSoapMessage = responseSoapMessageFile.read()
response = Soap.extractFromMessage(responseSoapMessage)
responseDom = libxml2.parseMemory(response, len(response))
element = SchemaDom.importFromNode(responseDom, 1)
return element
def singleLogoutRequestUrl(self):
session = context.getVar('session')
if not session:
return accessForbidden()
peerHostName = session['peerHostName']
providerId = context.getVar('httpHostName')
# set the name identifier :
nameIdentifier = None
remoteProvider = self.isServiceProvider()
if remoteProvider:
# at service provider :
nameIdentifier = self.getLocalNameIdentifierServiceProvider(
peerHostName)
else:
# at identity provider :
remoteProvider = self.isIdentityProvider(peerHostName)
nameIdentifier = self.getPeerNameIdentifierIdentityProvider(
peerHostName)
if not nameIdentifier:
nameIdentifier = self.getLocalNameIdentifierIdentityProvider(
peerHostName)
# set the relay state :
relayState = 'http://' + context.getVar('httpHostName')
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.encryptRelayState(relayState, symmetricKey)
# Choose a profile supported by the peer provider :
supportedProfiles = remoteProvider.singleLogoutProtocolProfiles
if not supportedProfiles:
raise 'no supprted profiles - is none'
if len(supportedProfiles) == 0:
raise 'no supported profiles'
# FIXME : supported protocol profiles
chosenProfile = None
for supportedProfile in supportedProfiles:
if supportedProfile in Constants.protocolProfiles.keys():
chosenProfile = supportedProfile
break
if not chosenProfile:
raise 'no protocol profile supported'
if chosenProfile in ('sloIdpHttp', 'sloSpHttp'):
# HTTP profile request :
handlerUrl = remoteProvider.singleLogoutServiceUrl
# must verify that handlerUrl starts with https:// :
if not handlerUrl.startswith('https://'):
raise 'HTTP unsecured is not allowed'
url = SingleLogout.getSingleLogoutHandlerUrl(
handlerUrl, providerId, nameIdentifier,
relayState = relayState)
return redirect(url)
if chosenProfile in ('sloIdpSoap', 'sloSpSoap'):
# SOAP profile request :
request = SingleLogout.buildSingleLogoutRequest(
providerId, nameIdentifier, relayState = relayState)
response = self.sendSoapRequest(request)
statusKey = response.getStatusCode()
if statusKey == 'Success':
# everything is ok, so delete the session
self.deleteSession()
relayState = response.getRelayState()
if 0: # was relayState: FIXME: crypt doesn't work
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.decryptRelayState(
relayState, symmetricKey)
return redirect(relayState)
# FIXME : redirect to relaystate
return OK
raise 'no profile found'
singleLogoutRequestUrl.isPublicForWeb = 1
def singleLogoutServiceReturnUrl(self, **keywords):
singleLogoutResponse = SingleLogout.buildSingleLogoutResponseFromKeywords(
keywords)
statusKey = singleLogoutResponse.getStatusCode()
if statusKey == 'Success':
# everything is ok, so delete the session
self.deleteSession()
relayState = singleLogoutResponse.getRelayState()
if 0: # was relayState: FIXME: crypt doesn't work
symmetricKey = self.getSymmetricKey()
relayState = lassoTools.decryptRelayState(
relayState, symmetricKey)
return redirect(relayState)
return OK
singleLogoutServiceReturnUrl.isPublicForWeb = 1
def singleLogoutServiceUrl(self, **keywords):
# process the request :
request = SingleLogout.buildSingleLogoutRequestFromKeywords(keywords)
response = self.processSingleLogoutRequest(request)
remoteProvider = self.isServiceProvider()
if not remoteProvider:
peerHostName = request.getProviderID()
remoteProvider = self.isIdentityProvider(peerHostName)
handlerUrl = remoteProvider.singleLogoutServiceReturnUrl
providerId = context.getVar('httpHostName')
url = SingleLogout.getSingleLogoutReturnHandlerUrl(
handlerUrl, providerId, response)
return redirect(url)
singleLogoutServiceUrl.isPublicForWeb = 1
def soapEndPoint(self):
"""Liberty Alliance SOAP Request Processing Method. """
requestSoapMessage = context.getVar('xmlPost')
import urllib
requestSoapMessage = urllib.unquote_plus(requestSoapMessage)
requestXml = Soap.extractFromMessage(requestSoapMessage)
requestDom = libxml2.parseMemory(requestXml, len(requestXml))
element = SchemaDom.extractElement(requestDom)
request = SchemaDom.importFromNode(requestDom, 1)
if element.name == 'Request':
response = self.processRequest(request)
elif element.name == 'RegisterNameIdentifierRequest':
response = self.processRegisterNameIdentifierRequest(request)
elif element.name == 'LogoutRequest':
response = self.processSingleLogoutRequest(request)
elif element.name ==' FederationTerminationNotification':
response = self.processFederationTerminationNotification(request)
if type(response) == type(HTTP_NO_CONTENT):
responseSoapMessage = 'http_no_content'
req = context.getVar('req')
req.content_type = 'text/xml'
req.send_http_header()
req.write(responseSoapMessage)
return HTTP_NO_CONTENT
responseSoapMessage = Soap.buildMessage(response.exportToString())
req = context.getVar('req')
req.content_type = 'text/xml'
req.send_http_header()
req.write(responseSoapMessage)
return OK
soapEndPoint.isPublicForWeb = 1
def status(self):
userId = context.getVar('userId')
if not userId:
return writePageLayout(X.p(_("You are not currently logged in.")),
_('Logon Status'))
return writePageLayout(X.p(X.asIs(
_('You are currently logged in as %s.') % \
X.objectHypertextLabel(userId))), _('Login Status'))
status.isPublicForWeb = 1