summaryrefslogtreecommitdiffstats
path: root/python
diff options
context:
space:
mode:
authorEmmanuel Raviart <eraviart@entrouvert.com>2004-08-09 16:19:45 +0000
committerEmmanuel Raviart <eraviart@entrouvert.com>2004-08-09 16:19:45 +0000
commita0a74c1a170f5e8e2c02bd52e43abfe598433a96 (patch)
treeca0de00de00271e46552e44b506965e7126c115b /python
parent710b77536cf42d6c43fddd45e0d717704520dfe6 (diff)
downloadlasso-a0a74c1a170f5e8e2c02bd52e43abfe598433a96.tar.gz
lasso-a0a74c1a170f5e8e2c02bd52e43abfe598433a96.tar.xz
lasso-a0a74c1a170f5e8e2c02bd52e43abfe598433a96.zip
Updated Python tests. Not finished but Valos want it to debug Lasso.
Diffstat (limited to 'python')
-rw-r--r--python/tests/IdentityProvider.py143
-rw-r--r--python/tests/LibertyEnabledClient.py21
-rw-r--r--python/tests/Provider.py10
-rw-r--r--python/tests/ServiceProvider.py139
-rw-r--r--python/tests/abstractweb.py53
-rw-r--r--python/tests/http.py600
-rw-r--r--python/tests/login_tests.py104
-rw-r--r--python/tests/websimulator.py154
8 files changed, 709 insertions, 515 deletions
diff --git a/python/tests/IdentityProvider.py b/python/tests/IdentityProvider.py
index 27ca0265..76cd3f0a 100644
--- a/python/tests/IdentityProvider.py
+++ b/python/tests/IdentityProvider.py
@@ -36,73 +36,74 @@ class IdentityProvider(Provider):
self.soapResponseMsgs = {}
def singleSignOn(self, handler):
- server = self.getServer()
+ lassoServer = self.getLassoServer()
if handler.httpRequest.method == 'GET':
# Single sign-on using HTTP redirect.
- login = lasso.Login.new(server)
- webSession = self.getWebSession(handler.httpRequest.client)
- webUser = None
- if webSession is not None:
- if webSession.sessionDump is not None:
- login.set_session_from_dump(webSession.sessionDump)
- webUser = self.getWebUserFromWebSession(webSession)
- if webUser is not None and webUser.identityDump is not None:
- login.set_identity_from_dump(webUser.identityDump)
+ login = lasso.Login.new(lassoServer)
+ session = handler.session
+ user = None
+ if session is not None:
+ if session.lassoSessionDump is not None:
+ login.set_session_from_dump(session.lassoSessionDump)
+ user = self.getUserFromSession(session)
+ if user is not None and user.lassoIdentityDump is not None:
+ login.set_identity_from_dump(user.lassoIdentityDump)
login.init_from_authn_request_msg(handler.httpRequest.query, lasso.httpMethodRedirect)
if not login.must_authenticate():
- userAuthenticated = webUser is not None
+ userAuthenticated = user is not None
authenticationMethod = lasso.samlAuthenticationMethodPassword # FIXME
return self.singleSignOn_part2(
- handler, login, webSession, webUser, userAuthenticated, authenticationMethod)
+ handler, login, session, user, userAuthenticated, authenticationMethod)
- if webSession is None:
- webSession = self.createWebSession(handler.httpRequest.client)
- webSession.loginDump = login.dump()
+ if session is None:
+ session = self.createSession(handler.httpRequest.client)
+ session.loginDump = login.dump()
# A real identity provider using a HTML form to ask user's login & password would store
# idpLoginDump in a session variable and display the HTML login form.
- webUserId = handler.httpRequest.client.keyring.get(self.url, None)
- userAuthenticated = webUserId in self.webUsers
+ userId = handler.httpRequest.client.keyring.get(self.url, None)
+ userAuthenticated = userId in self.users
if userAuthenticated:
- webSession.webUserId = webUserId
+ session.userId = userId
authenticationMethod = lasso.samlAuthenticationMethodPassword # FIXME
- server = self.getServer()
- webSession = self.getWebSession(handler.httpRequest.client)
- loginDump = webSession.loginDump
- del webSession.loginDump
- login = lasso.Login.new_from_dump(server, loginDump)
+ lassoServer = self.getLassoServer()
+ session = handler.session
+ loginDump = session.loginDump
+ del session.loginDump
+ login = lasso.Login.new_from_dump(lassoServer, loginDump)
# Set identity & session in login, because loginDump doesn't contain them.
- if webSession.sessionDump is not None:
- login.set_session_from_dump(webSession.sessionDump)
- webUser = self.getWebUserFromWebSession(webSession)
- if webUser is not None and webUser.identityDump is not None:
- login.set_identity_from_dump(webUser.identityDump)
+ if session.lassoSessionDump is not None:
+ login.set_session_from_dump(session.lassoSessionDump)
+ user = self.getUserFromSession(session)
+ if user is not None and user.lassoIdentityDump is not None:
+ login.set_identity_from_dump(user.lassoIdentityDump)
return self.singleSignOn_part2(
- handler, login, webSession, webUser, userAuthenticated, authenticationMethod)
+ handler, login, session, user, userAuthenticated, authenticationMethod)
elif handler.httpRequest.method == 'POST' \
and handler.httpRequest.headers.get('Content-Type', None) == 'text/xml':
# SOAP request => LECP single sign-on.
- lecp = lasso.Lecp.new(server)
- webSession = self.getWebSession(handler.httpRequest.client)
- webUser = None
- if webSession is not None:
- if webSession.sessionDump is not None:
- lecp.set_session_from_dump(webSession.sessionDump)
- webUser = self.getWebUserFromWebSession(webSession)
- if webUser is not None and webUser.identityDump is not None:
- lecp.set_identity_from_dump(webUser.identityDump)
+ lecp = lasso.Lecp.new(lassoServer)
+ session = handler.session
+ user = None
+ if session is not None:
+ if session.lassoSessionDump is not None:
+ lecp.set_session_from_dump(session.lassoSessionDump)
+ user = self.getUserFromSession(session)
+ if user is not None and user.lassoIdentityDump is not None:
+ lecp.set_identity_from_dump(user.lassoIdentityDump)
lecp.init_from_authn_request_msg(handler.httpRequest.body, lasso.httpMethodSoap)
# FIXME: lecp.must_authenticate() should always return False.
# The other solution is that we are able to not call lecp.must_authenticate()
# failIf(lecp.must_authenticate())
- userAuthenticated = webUser is not None
+ userAuthenticated = user is not None
authenticationMethod = lasso.samlAuthenticationMethodPassword # FIXME
# FIXME: It is very very strange that we don't provide userAuthenticated,
# authenticationMethod, reauthenticateOnOrAfter' to lecp before or when build response.
- lecp.build_authn_response_envelope_msg()
+ lecp.build_authn_response_envelope_msg(
+ userAuthenticated, authenticationMethod, 'FIXME: reauthenticateOnOrAfter')
soapResponseMsg = lecp.msg_body
failUnless(soapResponseMsg)
# FIXME: Lasso should set a lecp.msg_content_type to
@@ -123,7 +124,7 @@ class IdentityProvider(Provider):
400,
'Bad Request: Method %s not handled by singleSignOn' % handler.httpRequest.method)
- def singleSignOn_part2(self, handler, login, webSession, webUser, userAuthenticated,
+ def singleSignOn_part2(self, handler, login, session, user, userAuthenticated,
authenticationMethod):
failUnlessEqual(login.protocolProfile, lasso.loginProtocolProfileBrwsArt) # FIXME
login.build_artifact_msg(
@@ -131,17 +132,17 @@ class IdentityProvider(Provider):
lasso.httpMethodRedirect)
if userAuthenticated:
if login.is_identity_dirty():
- identityDump = login.get_identity().dump()
- failUnless(identityDump)
- webUser.identityDump = identityDump
+ lassoIdentityDump = login.get_identity().dump()
+ failUnless(lassoIdentityDump)
+ user.lassoIdentityDump = lassoIdentityDump
failUnless(login.is_session_dirty())
- sessionDump = login.get_session().dump()
- failUnless(sessionDump)
- webSession.sessionDump = sessionDump
+ lassoSessionDump = login.get_session().dump()
+ failUnless(lassoSessionDump)
+ session.lassoSessionDump = lassoSessionDump
nameIdentifier = login.nameIdentifier
failUnless(nameIdentifier)
- self.webUserIdsByNameIdentifier[nameIdentifier] = webUser.uniqueId
- self.webSessionIdsByNameIdentifier[nameIdentifier] = webSession.uniqueId
+ self.userIdsByNameIdentifier[nameIdentifier] = user.uniqueId
+ self.sessionTokensByNameIdentifier[nameIdentifier] = session.token
else:
failIf(login.is_identity_dirty())
failIf(login.is_session_dirty())
@@ -158,8 +159,8 @@ class IdentityProvider(Provider):
soapRequestMsg = handler.httpRequest.body
requestType = lasso.get_request_type_from_soap_msg(soapRequestMsg)
if requestType == lasso.requestTypeLogin:
- server = self.getServer()
- login = lasso.Login.new(server)
+ lassoServer = self.getLassoServer()
+ login = lasso.Login.new(lassoServer)
login.process_request_msg(soapRequestMsg)
artifact = login.assertionArtifact
failUnless(artifact)
@@ -169,49 +170,49 @@ class IdentityProvider(Provider):
return handler.respond(
headers = {'Content-Type': 'text/xml'}, body = soapResponseMsg)
elif requestType == lasso.requestTypeLogout:
- server = self.getServer()
- logout = lasso.Logout.new(server, lasso.providerTypeIdp)
+ lassoServer = self.getLassoServer()
+ logout = lasso.Logout.new(lassoServer, lasso.providerTypeIdp)
logout.process_request_msg(soapRequestMsg, lasso.httpMethodSoap)
nameIdentifier = logout.nameIdentifier
failUnless(nameIdentifier)
# Retrieve session dump and identity dump using name identifier.
- webSession = self.getWebSessionFromNameIdentifier(nameIdentifier)
- if webSession is None:
+ session = self.getSessionFromNameIdentifier(nameIdentifier)
+ if session is None:
raise Exception('FIXME: Handle the case when there is no web session')
- sessionDump = webSession.sessionDump
- if sessionDump is None:
+ lassoSessionDump = session.lassoSessionDump
+ if lassoSessionDump is None:
raise Exception(
'FIXME: Handle the case when there is no session dump in web session')
- logout.set_session_from_dump(sessionDump)
- webUser = self.getWebUserFromNameIdentifier(nameIdentifier)
- if webUser is None:
+ logout.set_session_from_dump(lassoSessionDump)
+ user = self.getUserFromNameIdentifier(nameIdentifier)
+ if user is None:
raise Exception('FIXME: Handle the case when there is no web user')
- identityDump = webUser.identityDump
- if identityDump is None:
+ lassoIdentityDump = user.lassoIdentityDump
+ if lassoIdentityDump is None:
raise Exception(
'FIXME: Handle the case when there is no identity dump in web user')
- logout.set_identity_from_dump(identityDump)
+ logout.set_identity_from_dump(lassoIdentityDump)
logout.validate_request()
failIf(logout.is_identity_dirty())
- identity = logout.get_identity()
- failUnless(identity)
- identityDump = identity.dump()
- failUnless(identityDump)
+ lassoIdentity = logout.get_identity()
+ failUnless(lassoIdentity)
+ lassoIdentityDump = lassoIdentity.dump()
+ failUnless(lassoIdentityDump)
failUnless(logout.is_session_dirty())
# Log the user out.
# It is done before logout from other service providers, since we don't want to
# accept passive login connections inbetween.
- del webSession.sessionDump
- del webSession.webUserId
+ del session.lassoSessionDump
+ del session.userId
# We also delete the session, but it is not mandantory, since the user is logged out
# anyway.
- del self.webSessions[webSession.uniqueId]
+ del self.sessions[session.token]
nameIdentifier = logout.nameIdentifier
failUnless(nameIdentifier)
- del self.webSessionIdsByNameIdentifier[nameIdentifier]
+ del self.sessionTokensByNameIdentifier[nameIdentifier]
# Tell each other service provider to logout the user.
otherProviderId = logout.get_next_providerID()
diff --git a/python/tests/LibertyEnabledClient.py b/python/tests/LibertyEnabledClient.py
index 9ac5e1ee..9a02872b 100644
--- a/python/tests/LibertyEnabledClient.py
+++ b/python/tests/LibertyEnabledClient.py
@@ -69,29 +69,32 @@ class LibertyEnabledClient(WebClient):
# 'LIBV=urn:liberty:iff:2003-08,http://projectliberty.org/specs/v1'))
'Accept': ','.join((httpRequestHeaders['Accept'], 'application/vnd.liberty-request+xml'))
})
- # FIXME: Lasso should provide a way for Liberty-enabled client to create a "server" without
- # metadata, instead of using 'singleSignOnServiceUrl'.
+ # FIXME: Lasso should provide a way for Liberty-enabled client to create a "lassoServer"
+ # without metadata, instead of using 'singleSignOnServiceUrl'.
idpSingleSignOnServiceUrl = None
+ lassoServerDump = None
def __init__(self, internet):
WebClient.__init__(self, internet)
+ def getLassoServer(self):
+ return lasso.Server.new_from_dump(self.lassoServerDump)
+
def login(self, principal, site, path):
httpResponse = self.sendHttpRequestToSite(site, 'GET', path)
failUnlessEqual(
httpResponse.headers['Content-Type'], 'application/vnd.liberty-request+xml')
- lecp = lasso.Lecp.new(None)
+ lassoServer = self.getLassoServer()
+ lecp = lasso.Lecp.new(lassoServer)
authnRequestEnvelope = httpResponse.body
- # FIXME: I don't understand why authnRequestEnvelopeMsg is base64 encoded. I think this
- # is an error.
- import base64
- authnRequestEnvelope = base64.encodestring(authnRequestEnvelope)
lecp.process_authn_request_envelope_msg(authnRequestEnvelope)
- lecp.build_authn_request_msg()
# FIXME: The service provider could return an IDPList in authnRequestEnvelope, so that
# we verify that self.idpSingleSignOnServiceUrl belongs to one of them
+ lecp.build_authn_request_msg(self.idpSite.providerId)
+ failUnless(lecp.msg_url)
+ failUnless(lecp.msg_body)
httpResponse = self.sendHttpRequest(
- 'POST', self.idpSingleSignOnServiceUrl, headers = {'Content-Type': 'text/xml'},
+ 'POST', lecp.msg_url, headers = {'Content-Type': 'text/xml'},
body = lecp.msg_body)
failUnlessEqual(
httpResponse.headers.get('Content-Type', None), 'application/vnd.liberty-response+xml')
diff --git a/python/tests/Provider.py b/python/tests/Provider.py
index 53cb10c2..498c81fb 100644
--- a/python/tests/Provider.py
+++ b/python/tests/Provider.py
@@ -32,9 +32,9 @@ class Provider(WebSite):
httpResponseHeaders.update({
'Liberty-Enabled': 'LIBV=urn:liberty:iff:2003-08,http://projectliberty.org/specs/v1',
})
- serverDump = None
- webUserIdsByNameIdentifier = None
- webSessionIdsByNameIdentifier = None
+ lassoServerDump = None
+ sessionTokensByNameIdentifier = None
+ userIdsByNameIdentifier = None
- def getServer(self):
- return lasso.Server.new_from_dump(self.serverDump)
+ def getLassoServer(self):
+ return lasso.Server.new_from_dump(self.lassoServerDump)
diff --git a/python/tests/ServiceProvider.py b/python/tests/ServiceProvider.py
index 2fd518a3..e23a9cf2 100644
--- a/python/tests/ServiceProvider.py
+++ b/python/tests/ServiceProvider.py
@@ -32,8 +32,8 @@ class ServiceProvider(Provider):
idpSite = None # The identity provider, this service provider will use to authenticate users.
def assertionConsumer(self, handler):
- server = self.getServer()
- login = lasso.Login.new(server)
+ lassoServer = self.getLassoServer()
+ login = lasso.Login.new(lassoServer)
if handler.httpRequest.method == 'GET':
login.init_request(handler.httpRequest.query, lasso.httpMethodRedirect)
@@ -72,66 +72,67 @@ class ServiceProvider(Provider):
else:
return handler.respond(
400,
- 'Bad Request: Method %s not handled by assertionConsumer' % handler.httpRequest.method)
+ 'Bad Request: Method %s not handled by assertionConsumer'
+ % handler.httpRequest.method)
nameIdentifier = login.nameIdentifier
failUnless(nameIdentifier)
# Retrieve session dump, using name identifier or else try to use the client web session.
# If session dump exists, give it to Lasso, so that it updates it.
- webSession = self.getWebSessionFromNameIdentifier(nameIdentifier)
- if webSession is None:
- webSession = self.getWebSession(handler.httpRequest.client)
- if webSession is not None:
- sessionDump = webSession.sessionDump
- if sessionDump is not None:
- login.set_session_from_dump(sessionDump)
+ session = self.getSessionFromNameIdentifier(nameIdentifier)
+ if session is None:
+ session = handler.session
+ if session is not None:
+ lassoSessionDump = session.lassoSessionDump
+ if lassoSessionDump is not None:
+ login.set_session_from_dump(lassoSessionDump)
# Retrieve identity dump, using name identifier or else try to retrieve him from web
# session. If identity dump exists, give it to Lasso, so that it updates it.
- webUser = self.getWebUserFromNameIdentifier(nameIdentifier)
- if webUser is None:
- webUser = self.getWebUserFromWebSession(webSession)
- if webUser is not None:
- identityDump = webUser.identityDump
- if identityDump is not None:
- login.set_identity_from_dump(identityDump)
+ user = self.getUserFromNameIdentifier(nameIdentifier)
+ if user is None:
+ user = self.getUserFromSession(session)
+ if user is not None:
+ lassoIdentityDump = user.lassoIdentityDump
+ if lassoIdentityDump is not None:
+ login.set_identity_from_dump(lassoIdentityDump)
login.accept_sso()
- if webUser is not None and identityDump is None:
+ if user is not None and lassoIdentityDump is None:
failUnless(login.is_identity_dirty())
- identity = login.get_identity()
- failUnless(identity)
- identityDump = identity.dump()
- failUnless(identityDump)
+ lassoIdentity = login.get_identity()
+ failUnless(lassoIdentity)
+ lassoIdentityDump = lassoIdentity.dump()
+ failUnless(lassoIdentityDump)
failUnless(login.is_session_dirty())
- session = login.get_session()
- failUnless(session)
- sessionDump = session.dump()
- failUnless(sessionDump)
+ lassoSession = login.get_session()
+ failUnless(lassoSession)
+ lassoSessionDump = lassoSession.dump()
+ failUnless(lassoSessionDump)
# User is now authenticated.
# If there was no web session yet, create it. Idem for the web user account.
- if webSession is None:
- webSession = self.createWebSession(handler.httpRequest.client)
- if webUser is None:
+ if session is None:
+ session = self.createSession(handler.httpRequest.client)
+ if user is None:
# A real service provider would ask user to login locally to create federation. Or it
# would ask user informations to create a local account.
- webUserId = handler.httpRequest.client.keyring.get(self.url, None)
- userAuthenticated = webUserId in self.webUsers
+ userId = handler.httpRequest.client.keyring.get(self.url, None)
+ userAuthenticated = userId in self.users
if not userAuthenticated:
return handler.respond(401, 'Access Unauthorized: User has no account.')
- webUser = self.webUsers[webUserId]
+ user = self.users[userId]
- webSession.webUserId = webUser.uniqueId
+ session.userId = user.uniqueId
# Store the updated identity dump and session dump.
if login.is_identity_dirty():
- webUser.identityDump = identityDump
- webSession.sessionDump = sessionDump
+ user.lassoIdentityDump = lassoIdentityDump
+ session.lassoSessionDump = lassoSessionDump
- self.webUserIdsByNameIdentifier[nameIdentifier] = webUser.uniqueId
- self.webSessionIdsByNameIdentifier[nameIdentifier] = webSession.uniqueId
+ self.userIdsByNameIdentifier[nameIdentifier] = user.uniqueId
+ self.sessionTokensByNameIdentifier[nameIdentifier] = session.token
return handler.respond()
@@ -139,7 +140,7 @@ class ServiceProvider(Provider):
libertyEnabled = handler.httpRequest.headers.get('Liberty-Enabled', None)
userAgent = handler.httpRequest.headers.get('User-Agent', None)
# FIXME: Lasso should have a function to compute useLecp.
- # Or this should be done in lasso.Login.new(server, libertyEnabled, userAgent)
+ # Or this should be done in lasso.Login.new(lassoServer, libertyEnabled, userAgent)
useLecp = False
if libertyEnabled:
useLecp = 'urn:liberty:iff:2003-08' in libertyEnabled
@@ -154,10 +155,10 @@ class ServiceProvider(Provider):
forceAuthn = handler.httpRequest.getQueryBoolean('forceAuthn', False)
isPassive = handler.httpRequest.getQueryBoolean('isPassive', False)
- server = self.getServer()
+ lassoServer = self.getLassoServer()
if useLecp:
- lecp = lasso.Lecp.new(server)
- lecp.init_authn_request(self.idpSite.providerId) # FIXME: The argument should be None.
+ lecp = lasso.Lecp.new(lassoServer)
+ lecp.init_authn_request()
failUnlessEqual(lecp.request_type, lasso.messageTypeAuthnRequest)
# FIXME: This protocol profile should be set by default by Lasso.
@@ -173,12 +174,8 @@ class ServiceProvider(Provider):
relayState = 'fake'
lecp.request.set_relayState(relayState)
- # FIXME: In my opinion, this method should be the renamed to build_authn_request_msg.
lecp.build_authn_request_envelope_msg()
authnRequestEnvelopeMsg = lecp.msg_body
- # FIXME: I don't understand why authnRequestEnvelopeMsg is base64 encoded.
- import base64
- authnRequestEnvelopeMsg = base64.decodestring(authnRequestEnvelopeMsg)
failUnless(authnRequestEnvelopeMsg)
# FIXME: Lasso should set a lecp.msg_content_type to
# "application/vnd.liberty-request+xml". This should also be done for SOAP, etc, with
@@ -194,8 +191,8 @@ class ServiceProvider(Provider):
},
body = authnRequestEnvelopeMsg)
else:
- login = lasso.Login.new(server)
- login.init_authn_request(self.idpSite.providerId)
+ login = lasso.Login.new(lassoServer)
+ login.init_authn_request()
failUnlessEqual(login.request_type, lasso.messageTypeAuthnRequest)
if forceAuthn:
login.request.set_forceAuthn(forceAuthn)
@@ -205,27 +202,27 @@ class ServiceProvider(Provider):
login.request.set_consent(lasso.libConsentObtained)
relayState = 'fake'
login.request.set_relayState(relayState)
- login.build_authn_request_msg()
+ login.build_authn_request_msg(self.idpSite.providerId)
authnRequestUrl = login.msg_url
failUnless(authnRequestUrl)
return handler.respondRedirectTemporarily(authnRequestUrl)
def logoutUsingSoap(self, handler):
- webSession = self.getWebSession(handler.httpRequest.client)
- if webSession is None:
+ session = handler.session
+ if session is None:
return handler.respond(401, 'Access Unauthorized: User has no session opened.')
- webUser = self.getWebUserFromWebSession(webSession)
- if webUser is None:
+ user = self.getUserFromSession(session)
+ if user is None:
return handler.respond(401, 'Access Unauthorized: User is not logged in.')
- server = self.getServer()
- logout = lasso.Logout.new(server, lasso.providerTypeSp)
- identityDump = self.getIdentityDump(handler.httpRequest.client)
- if identityDump is not None:
- logout.set_identity_from_dump(identityDump)
- sessionDump = self.getSessionDump(handler.httpRequest.client)
- if sessionDump is not None:
- logout.set_session_from_dump(sessionDump)
+ lassoServer = self.getLassoServer()
+ logout = lasso.Logout.new(lassoServer, lasso.providerTypeSp)
+ lassoIdentityDump = self.getIdentityDump(handler.httpRequest.client)
+ if lassoIdentityDump is not None:
+ logout.set_identity_from_dump(lassoIdentityDump)
+ lassoSessionDump = self.getLassoSessionDump(handler.httpRequest.client)
+ if lassoSessionDump is not None:
+ logout.set_session_from_dump(lassoSessionDump)
logout.init_request()
logout.build_request_msg()
@@ -241,24 +238,24 @@ class ServiceProvider(Provider):
failIf(logout.is_identity_dirty())
identity = logout.get_identity()
failUnless(identity)
- identityDump = identity.dump()
- failUnless(identityDump)
+ lassoIdentityDump = identity.dump()
+ failUnless(lassoIdentityDump)
failUnless(logout.is_session_dirty())
- session = logout.get_session()
- if session is None:
+ lassoSession = logout.get_session()
+ if lassoSession is None:
# The user is no more authenticated on any identity provider. Log him out.
- del webSession.sessionDump
- del webSession.webUserId
+ del session.lassoSessionDump
+ del session.userId
# We also delete the session, but it is not mandantory, since the user is logged out
# anyway.
- del self.webSessions[webSession.uniqueId]
+ del self.sessions[session.token]
else:
# The user is still logged in on some other identity providers.
- sessionDump = session.dump()
- failUnless(sessionDump)
- webSession.sessionDump = sessionDump
+ lassoSessionDump = lassoSession.dump()
+ failUnless(lassoSessionDump)
+ session.lassoSessionDump = lassoSessionDump
nameIdentifier = logout.nameIdentifier
failUnless(nameIdentifier)
- del self.webSessionIdsByNameIdentifier[nameIdentifier]
+ del self.sessionTokensByNameIdentifier[nameIdentifier]
return handler.respond()
diff --git a/python/tests/abstractweb.py b/python/tests/abstractweb.py
index 14eaced3..6c80ff7a 100644
--- a/python/tests/abstractweb.py
+++ b/python/tests/abstractweb.py
@@ -35,28 +35,35 @@ class HttpRequestMixin:
query = None
scheme = None # 'http' or 'https'
- def getFormField(self, name, default = 'none'):
+ def getFormField(self, name, default = None):
raise NotImplementedError
- def getQueryBoolean(self, name, default = 'none'):
- try:
- fieldValue = self.getQueryField(name)
- except KeyError:
- if default == 'none':
- raise
+ def getQueryBoolean(self, name, default = None):
+ fieldValue = self.getQueryField(name, 'none')
+ if fieldValue == 'none':
return default
- return fieldValue.lower not in ('', '0', 'false')
+ else:
+ return fieldValue.lower not in ('', '0', 'false')
- def getQueryField(self, name, default = 'none'):
+ def getQueryField(self, name, default = None):
if self.query:
for field in self.query.split('&'):
fieldName, fieldValue = field.split('=')
if name == fieldName:
return fieldValue
- if default == 'none':
- raise KeyError(name)
return default
+ def hasFormField(self, name):
+ raise NotImplementedError
+
+ def hasQueryField(self, name):
+ if self.query:
+ for field in self.query.split('&'):
+ fieldName, fieldValue = field.split('=')
+ if name == fieldName:
+ return True
+ return False
+
class HttpResponseMixin:
body = None
@@ -113,7 +120,7 @@ class HttpResponseMixin:
self.statusMessage = statusMessage
else:
self.statusMessage = self.defaultStatusMessages.get(statusCode)
- httpResponseHeaders = httpRequestHandler.webServer.httpResponseHeaders
+ httpResponseHeaders = httpRequestHandler.site.httpResponseHeaders
if headers:
httpResponseHeaders = httpResponseHeaders.copy()
for name, value in headers.iteritems():
@@ -133,7 +140,7 @@ class HttpRequestHandlerMixin:
httpResponse = None
session = None
user = None
- webServer = None # The web server, which can then redirect to several virtual hosts
+ site = None # The virtual host
def respond(self, statusCode = 200, statusMessage = None, headers = None, body = None):
# Session must be saved before responding. Otherwise, when the server is multitasked or
@@ -154,3 +161,23 @@ class HttpRequestHandlerMixin:
def respondRedirectTemporarily(self, url):
raise NotImplementedError
+
+
+class WebSessionMixin:
+ publishToken = False
+ token = None
+
+
+class WebSiteMixin:
+ def authenticateX509User(self, clientCertificate):
+ # We should check certificate (for example clientCertificate.get_serial_number()
+ # and return the user if one matches, or None otherwise.
+ return None
+
+ def authenticateLoginPasswordUser(self, login, password):
+ # We should check login & password and return the user if one matches or None otherwise.
+ return None
+
+
+class WebUserMixin:
+ sessionToken = None
diff --git a/python/tests/http.py b/python/tests/http.py
index fca7eec7..301f0393 100644
--- a/python/tests/http.py
+++ b/python/tests/http.py
@@ -274,7 +274,7 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
# requestline = None # Strange
# request_version = None # Strange
server_version = 'HttpRequestHandlerMixin/1.0'
- webServer = None # Class variable.
+ site = None # Class variable.
def handle(self):
"""Handle multiple requests if necessary."""
@@ -313,11 +313,186 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
return
logger.info(self.raw_requestline.strip())
logger.debug(str(self.headers))
-## if hasattr(self.connection, 'get_peer_certificate'):
-## peerCertificate = self.connection.get_peer_certificate()
+
+###############
+
+ session = None
+ sessionToken = None
+ user = None
+
+ # Handle X.509 certificate authentication.
+ if hasattr(self.connection, 'get_peer_certificate'):
+ clientCertificate = self.connection.get_peer_certificate()
+ if clientCertificate:
+ user = self.site.authenticateX509User(clientCertificate)
+ if user is None:
+ logger.info('Unknown certificate (serial number = %s)'
+ % clientCertificate.get_serial_number())
+ else:
+ sessionToken = user.sessionToken
+ if sessionToken:
+ session = self.site.getSessionFromToken(sessionToken)
+ if session is None:
+ sessionToken = None
+ del user.sessionToken
+ # Don't call user.getDocument().save() now, because the session
+ # will be retrieved (from cookie or sessionToken query field)
+ # or created. So user/@sessionToken will be updated.
+ else:
+ # For security reasons, we want to minimize the publication of
+ # session token (it is better not to store it in a cookie or in
+ # URLs). The client need to send the certificate each time, for the
+ # session to continue.
+ if session.publishToken:
+ del session.publishToken
+
+ # Handle HTTP authentication.
+ authorization = self.httpRequest.headers.get('authorization')
+ if self.httpRequest.hasQueryField('login') and not authorization \
+ and rootDataHolder.getConfigBoolean('yep:useHttpAuthentication', default = False):
+ # Ask for HTTP authentication.
+ return self.outputErrorUnauthorized(httpPath)
+ if self.httpRequest.hasQueryField('logout') and authorization:
+ # Since HTTP authentication provides no way to logout, we send a status
+ # Unauthorized to force the user to press the cancel button. But instead of
+ # sending an error page immediately, we send the real page, so the user will see
+ # the page instead of an error message.
+ authorization = None
+ self.httpAuthenticationLogoutTrick = True
+ if authorization:
+ try:
+ authenticationScheme, credentials = authorization.split(None, 1)
+ except ValueError:
+ return self.outputErrorUnauthorized(httpPath)
+ authenticationScheme = authenticationScheme.lower()
+ if authenticationScheme == 'basic':
+ loginAndPassword = base64.decodestring(credentials)
+ try:
+ login, password = loginAndPassword.split(':', 1)
+ except:
+ login = loginAndPassword
+ password = ''
+ logs.debug('Basic authentication: login = "%s" / password = "%s"' % (
+ login, password))
+ if password:
+ user = self.site.authenticateLoginPasswordUser(login, password)
+ if user is None:
+ logger.info('Unknown user (login = "%s" / password = "%s")' % (
+ login, password))
+ return self.outputErrorUnauthorized(httpPath)
+ else:
+ sessionToken = user.sessionToken
+ if sessionToken:
+ session = sessions.retrieveSession(sessionToken)
+ if session is None:
+ sessionToken = None
+ user.deleteSessionToken()
+ # Don't call user.getDocument().save() now, because the session
+ # will be retrieved (from cookie or sessionToken query field)
+ # or created. So user/@sessionToken will be updated.
+ else:
+ # For security reasons, we want to minimize the publication of
+ # session token (it is better not to store it in a cookie or in
+ # URLs).
+ if session.publishToken:
+ del session.publishToken
+ elif login:
+ # No password was given. Assume login contains a session token.
+ # TODO: sanity chek on login
+ session = sessions.retrieveSession(login)
+ if session is not None:
+ account = session.getAccount()
+ if account is not None:
+ user = account.getUser()
+ if user is not None \
+ and user.getSessionToken() != session.getToken():
+ # Sanity check.
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ else:
+ logs.info('Unknown authentication scheme = %s' % authenticationScheme)
+ return self.outputErrorUnauthorized(httpPath)
+
+ # Handle use of cookies, session and user.
+ cookie = None
+ cookieContent = {}
+ if self.httpRequest.headers.has_key('Cookie'):
+ logs.debug('Cookie received:')
+ cookie = Cookie.SimpleCookie(
+ self.httpRequest.headers['Cookie'])
+ for k, v in cookie.items():
+ cookieContent[k] = v.value
+ logs.debug(' %s = %s' % (k, cookieContent[k]))
+ self.cookie = cookie
+
+ sessionToken = None
+ sessionTokenInCookie = False
+ if self.httpRequest.hasQueryField('sessionToken'):
+ sessionToken = self.httpRequest.getQueryField('sessionToken')
+ if not sessionToken:
+ sessionToken = None
+ if session is not None and sessionToken != session.token:
+ sessionToken = None
+ if cookieContent.has_key('sessionToken'):
+ cookieSessionToken = cookieContent['sessionToken']
+ if cookieSessionToken:
+ if session is None or cookieSessionToken == session.token:
+ if sessionToken is None:
+ sessionToken = cookieSessionToken
+ if cookieSessionToken == sessionToken:
+ sessionTokenInCookie = True
+ canUseCookie = True
+ if session is None and sessionToken is not None:
+ session = sessions.retrieveSession(sessionToken)
+ if session is None:
+ sessionToken = None
+ sessionTokenInCookie = False
+ else:
+ if user is None:
+ # import expression.modules.passwordaccounts as passwordaccounts
+ account = session.getAccount(acceptOnlyAccount = False)
+ if account is not None:
+ user = account.getUser()
+ if user is not None and user.getSessionToken() != sessionToken:
+ # Sanity check.
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ else:
+ # The user has been authenticated (using HTTP authentication), but the
+ # associated session didn't exist (or was too old, or...). So, update
+ # its sessionToken.
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ # For security reasons, we want to minimize the publication of session
+ # token (it is better not to store it in a cookie or in URLs).
+ if session.publishToken:
+ del session.publishToken
+ if session is None and user is not None:
+ # The user has been authenticated (using HTTP authentication), but the session
+ # doesn't exist yet (or was too old, or...). Create a new session.
+ session = sessions.getOrCreateSession()
+ # For security reasons, we want to minimize the publication of session
+ # token (it is better not to store it in a cookie or in URLs).
+ # session.publishToken = False # False is the default value.
+ session.setAccountAbsolutePath(account.getAbsolutePath())
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ self.user = user
+ if user is not None:
+ logs.debug('User: %s' % user.simpleLabel)
+ self.session = session
+ if session is not None:
+ if not sessionTokenInCookie:
+ # The sessionToken is valid but is not stored in the cookie. So, don't try to
+ # use cookie.
+ canUseCookie = False
+ logs.debug('Session: %s' % session.token)
+ self.canUseCookie = canUseCookie
+
+###############
try:
- self.webServer.handleHttpRequestHandler(self)
+ self.site.handleHttpRequestHandler(self)
except IOError:
logger.exception('An exception occured:')
path = self.path.split('?')[0]
@@ -332,146 +507,146 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
logger.info('%s - - [%s] %s' % (
self.address_string(), self.log_date_time_string(), format % arguments))
- def outputAlert(self, data, title = None, url = None):
- import html
- if title is None:
- title = N_('Alert')
- # FIXME: Handle XSLT template.
- if url:
- buttonsBar = html.div(class_ = 'buttons-bar')
- actionButtonsBar = html.span(class_ = 'action-buttons-bar')
- buttonsBar.append(actionButtonsBar)
- actionButtonsBar.append(html.a(_('OK'), class_ = 'button', href = url))
- else:
- buttonsBar = None
- layout = html.html(
- html.head(html.title(_(title))),
- html.body(
- html.p(_(data), class_ = 'alert'),
- buttonsBar,
- ),
- )
- self.outputData(layout.serialize(), contentLocation = None, mimeType = 'text/html')
-
- def outputData(self, data, contentLocation = None, headers = None, mimeType = None,
- modificationTime = None, successCode = 200):
- # Session must be saved before responding. Otherwise, when the server is multitasked or
- # multithreaded, it may receive a new HTTP request before the session is saved.
- if self.session is not None and self.session.isDirty:
- self.session.save()
-
- if isinstance(data, basestring):
- dataFile = None
- dataSize = len(data)
- else:
- dataFile = data
- data = ''
- if hasattr(dataFile, 'fileno'):
- dataSize = os.fstat(dataFile.fileno())[6]
- else:
- # For StringIO and cStringIO classes.
- dataSize = len(dataFile.getvalue())
-
- if headers is None:
- headers = {}
- if time.time() > self.socketCreationTime + 300:
- headers['Connection'] = 'close'
- elif not self.close_connection:
- headers['Connection'] = 'Keep-Alive'
- if contentLocation is not None:
- headers['Content-Location'] = contentLocation
- if mimeType:
- headers['Content-Type'] = '%s; charset=utf-8' % mimeType
- if modificationTime:
- headers['Last-Modified'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', modificationTime)
- # TODO: Could also output Content-MD5.
- ifModifiedSince = self.headers.get('If-Modified-Since')
- if modificationTime and ifModifiedSince:
- # We don't want to use bandwith if the file was not modified.
- try:
- ifModifiedSinceTime = time.strptime(ifModifiedSince[:25], '%a, %d %b %Y %H:%M:%S')
- if modificationTime[:8] <= ifModifiedSinceTime[:8]:
- self.send_response(304, 'Not Modified.')
- for key in ('Connection', 'Content-Location'):
- if key in headers:
- self.send_header(key, headers[key])
- self.setCookie()
- self.end_headers()
- return
- except (ValueError, KeyError):
- pass
- if dataFile is not None:
- assert not data
- data = dataFile.read(1048576) # Read first MB chunk
- if mimeType == 'text/html' and data.startswith('<?xml'):
- # Internet Explorer 6 renders the page differently when they start with <?xml...>, so
- # skip it.
- i = data.find('\n')
- if i > 0:
- data = data[i + 1:]
- else:
- i = data.find('>')
- if i > 0:
- data = data[i + 1:]
- dataSize -= i + 1
- # Compress data if possible and if data is not too big.
- acceptEncoding = self.headers.get('Accept-Encoding', '')
- if 0 < dataSize < 1048576 and 'gzip' in acceptEncoding \
- and 'gzip;q=0' not in acceptEncoding:
- # Since dataSize < 1 MB, the data is fully contained in string.
- zbuf = cStringIO.StringIO()
- zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf)
- zfile.write(data)
- zfile.close()
- data = zbuf.getvalue()
- dataSize = len(data)
- headers['Content-Encoding'] = 'gzip'
- headers['Content-Length'] = '%d' % dataSize
- successMessages = {
- 200: 'OK',
- 207: 'Multi-Status',
- }
- assert successCode in successMessages, 'Unknown success code %d.' % successCode
- if self.httpAuthenticationLogoutTrick and successCode == 200:
- successCode = 401
- successMessage = 'Access Unauthorized'
- headers['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
- else:
- successMessage = successMessages[successCode]
- self.send_response(successCode, successMessage)
- for key, value in headers.items():
- self.send_header(key, value)
- self.setCookie()
- self.end_headers()
- if self.httpRequest.method != 'HEAD' and dataSize > 0:
- outputFile = self.wfile
- if data:
- outputFile.write(data)
- if dataFile is not None:
- while True:
- chunk = dataFile.read(1048576) # 1 MB chunk
- if not chunk:
- break
- outputFile.write(chunk)
- return
-
- def outputErrorAccessForbidden(self, filePath):
- if filePath is None:
- message = 'Access Forbidden'
- else:
- message = 'Access to "%s" Forbidden.' % filePath
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- return self.send_error(403, message, data, setCookie = True)
-
- def outputErrorBadRequest(self, reason):
- if reason:
- message = 'Bad Request: %s' % reason
- else:
- message = 'Bad Request'
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- return self.send_error(400, message, data)
+## def outputAlert(self, data, title = None, url = None):
+## import html
+## if title is None:
+## title = N_('Alert')
+## # FIXME: Handle XSLT template.
+## if url:
+## buttonsBar = html.div(class_ = 'buttons-bar')
+## actionButtonsBar = html.span(class_ = 'action-buttons-bar')
+## buttonsBar.append(actionButtonsBar)
+## actionButtonsBar.append(html.a(_('OK'), class_ = 'button', href = url))
+## else:
+## buttonsBar = None
+## layout = html.html(
+## html.head(html.title(_(title))),
+## html.body(
+## html.p(_(data), class_ = 'alert'),
+## buttonsBar,
+## ),
+## )
+## self.outputData(layout.serialize(), contentLocation = None, mimeType = 'text/html')
+
+## def outputData(self, data, contentLocation = None, headers = None, mimeType = None,
+## modificationTime = None, successCode = 200):
+## # Session must be saved before responding. Otherwise, when the server is multitasked or
+## # multithreaded, it may receive a new HTTP request before the session is saved.
+## if self.session is not None and self.session.isDirty:
+## self.session.save()
+
+## if isinstance(data, basestring):
+## dataFile = None
+## dataSize = len(data)
+## else:
+## dataFile = data
+## data = ''
+## if hasattr(dataFile, 'fileno'):
+## dataSize = os.fstat(dataFile.fileno())[6]
+## else:
+## # For StringIO and cStringIO classes.
+## dataSize = len(dataFile.getvalue())
+
+## if headers is None:
+## headers = {}
+## if time.time() > self.socketCreationTime + 300:
+## headers['Connection'] = 'close'
+## elif not self.close_connection:
+## headers['Connection'] = 'Keep-Alive'
+## if contentLocation is not None:
+## headers['Content-Location'] = contentLocation
+## if mimeType:
+## headers['Content-Type'] = '%s; charset=utf-8' % mimeType
+## if modificationTime:
+## headers['Last-Modified'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', modificationTime)
+## # TODO: Could also output Content-MD5.
+## ifModifiedSince = self.headers.get('If-Modified-Since')
+## if modificationTime and ifModifiedSince:
+## # We don't want to use bandwith if the file was not modified.
+## try:
+## ifModifiedSinceTime = time.strptime(ifModifiedSince[:25], '%a, %d %b %Y %H:%M:%S')
+## if modificationTime[:8] <= ifModifiedSinceTime[:8]:
+## self.send_response(304, 'Not Modified.')
+## for key in ('Connection', 'Content-Location'):
+## if key in headers:
+## self.send_header(key, headers[key])
+## self.setCookie()
+## self.end_headers()
+## return
+## except (ValueError, KeyError):
+## pass
+## if dataFile is not None:
+## assert not data
+## data = dataFile.read(1048576) # Read first MB chunk
+## if mimeType == 'text/html' and data.startswith('<?xml'):
+## # Internet Explorer 6 renders the page differently when they start with <?xml...>, so
+## # skip it.
+## i = data.find('\n')
+## if i > 0:
+## data = data[i + 1:]
+## else:
+## i = data.find('>')
+## if i > 0:
+## data = data[i + 1:]
+## dataSize -= i + 1
+## # Compress data if possible and if data is not too big.
+## acceptEncoding = self.headers.get('Accept-Encoding', '')
+## if 0 < dataSize < 1048576 and 'gzip' in acceptEncoding \
+## and 'gzip;q=0' not in acceptEncoding:
+## # Since dataSize < 1 MB, the data is fully contained in string.
+## zbuf = cStringIO.StringIO()
+## zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf)
+## zfile.write(data)
+## zfile.close()
+## data = zbuf.getvalue()
+## dataSize = len(data)
+## headers['Content-Encoding'] = 'gzip'
+## headers['Content-Length'] = '%d' % dataSize
+## successMessages = {
+## 200: 'OK',
+## 207: 'Multi-Status',
+## }
+## assert successCode in successMessages, 'Unknown success code %d.' % successCode
+## if self.httpAuthenticationLogoutTrick and successCode == 200:
+## successCode = 401
+## successMessage = 'Access Unauthorized'
+## headers['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
+## else:
+## successMessage = successMessages[successCode]
+## self.send_response(successCode, successMessage)
+## for key, value in headers.items():
+## self.send_header(key, value)
+## self.setCookie()
+## self.end_headers()
+## if self.httpRequest.method != 'HEAD' and dataSize > 0:
+## outputFile = self.wfile
+## if data:
+## outputFile.write(data)
+## if dataFile is not None:
+## while True:
+## chunk = dataFile.read(1048576) # 1 MB chunk
+## if not chunk:
+## break
+## outputFile.write(chunk)
+## return
+
+## def outputErrorAccessForbidden(self, filePath):
+## if filePath is None:
+## message = 'Access Forbidden'
+## else:
+## message = 'Access to "%s" Forbidden.' % filePath
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## return self.send_error(403, message, data, setCookie = True)
+
+## def outputErrorBadRequest(self, reason):
+## if reason:
+## message = 'Bad Request: %s' % reason
+## else:
+## message = 'Bad Request'
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## return self.send_error(400, message, data)
def outputErrorInternalServer(self):
message = 'Internal Server Error'
@@ -479,71 +654,71 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
data = '<html><body>%s</body></html>' % message
return self.send_error(500, message, data)
- def outputErrorMethodNotAllowed(self, reason):
- if reason:
- message = 'Method Not Allowed: %s' % reason
- else:
- message = 'Method Not Allowed'
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- # This error doesn't need a pretty interface.
- # FIXME: Add an 'Allow' header containing a list of valid methods for the requested
- # resource.
- return self.send_error(405, message, data)
-
- def outputErrorNotFound(self, filePath):
- if filePath is None:
- message = 'Not Found'
- else:
- message = 'Path "%s" Not Found.' % filePath
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- return self.send_error(404, message, data, setCookie = True)
-
- def outputErrorUnauthorized(self, filePath):
- if filePath is None:
- message = 'Access Unauthorized'
- else:
- message = 'Access to "%s" Unauthorized.' % filePath
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- headers = {}
- return self.send_error(401, message, data, headers, setCookie = True)
-
- def outputInformationContinue(self):
- message = 'Continue'
- logger.debug(message)
- self.send_response(100, message)
-
- def outputSuccessCreated(self, filePath):
- if filePath is None:
- message = 'Created'
- else:
- message = 'File "%s" Created.' % filePath
- logger.debug(message)
- data = '<html><body>%s</body></html>' % message
- self.send_response(201, message)
- if time.time() > self.socketCreationTime + 300:
- self.send_header('Connection', 'close')
- elif not self.close_connection:
- self.send_header('Connection', 'Keep-Alive')
- self.send_header('Content-Type', 'text/html; charset=utf-8')
- self.send_header('Content-Length', '%d' % len(data))
- self.setCookie()
- self.end_headers()
- if self.httpRequest.method != 'HEAD':
- self.wfile.write(data)
-
- def outputSuccessNoContent(self):
- message = 'No Content'
- logger.debug(message)
- self.send_response(204, message)
- if time.time() > self.socketCreationTime + 300:
- self.send_header('Connection', 'close')
- elif not self.close_connection:
- self.send_header('Connection', 'Keep-Alive')
- self.setCookie()
- self.end_headers()
+## def outputErrorMethodNotAllowed(self, reason):
+## if reason:
+## message = 'Method Not Allowed: %s' % reason
+## else:
+## message = 'Method Not Allowed'
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## # This error doesn't need a pretty interface.
+## # FIXME: Add an 'Allow' header containing a list of valid methods for the requested
+## # resource.
+## return self.send_error(405, message, data)
+
+## def outputErrorNotFound(self, filePath):
+## if filePath is None:
+## message = 'Not Found'
+## else:
+## message = 'Path "%s" Not Found.' % filePath
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## return self.send_error(404, message, data, setCookie = True)
+
+## def outputErrorUnauthorized(self, filePath):
+## if filePath is None:
+## message = 'Access Unauthorized'
+## else:
+## message = 'Access to "%s" Unauthorized.' % filePath
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## headers = {}
+## return self.send_error(401, message, data, headers, setCookie = True)
+
+## def outputInformationContinue(self):
+## message = 'Continue'
+## logger.debug(message)
+## self.send_response(100, message)
+
+## def outputSuccessCreated(self, filePath):
+## if filePath is None:
+## message = 'Created'
+## else:
+## message = 'File "%s" Created.' % filePath
+## logger.debug(message)
+## data = '<html><body>%s</body></html>' % message
+## self.send_response(201, message)
+## if time.time() > self.socketCreationTime + 300:
+## self.send_header('Connection', 'close')
+## elif not self.close_connection:
+## self.send_header('Connection', 'Keep-Alive')
+## self.send_header('Content-Type', 'text/html; charset=utf-8')
+## self.send_header('Content-Length', '%d' % len(data))
+## self.setCookie()
+## self.end_headers()
+## if self.httpRequest.method != 'HEAD':
+## self.wfile.write(data)
+
+## def outputSuccessNoContent(self):
+## message = 'No Content'
+## logger.debug(message)
+## self.send_response(204, message)
+## if time.time() > self.socketCreationTime + 300:
+## self.send_header('Connection', 'close')
+## elif not self.close_connection:
+## self.send_header('Connection', 'Keep-Alive')
+## self.setCookie()
+## self.end_headers()
def outputUnknownException(self):
import traceback, cStringIO
@@ -704,10 +879,7 @@ class HttpsRequestHandler(HttpRequestHandlerMixin, BaseHTTPSRequestHandler):
# not enough for a web server.
class HttpServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer):
- uriScheme = 'http'
- webServer = None
-
+ pass
class HttpsServer(SocketServer.ForkingMixIn, BaseHTTPSServer):
- uriScheme = 'https' # Ob
- webServer = None
+ pass
diff --git a/python/tests/login_tests.py b/python/tests/login_tests.py
index 47378f48..88034c3c 100644
--- a/python/tests/login_tests.py
+++ b/python/tests/login_tests.py
@@ -46,68 +46,62 @@ class LoginTestCase(unittest.TestCase):
site = IdentityProvider(internet, 'https://identity-provider/')
site.providerId = 'https://identity-provider/metadata'
- server = lasso.Server.new(
+ lassoServer = lasso.Server.new(
'../../examples/data/idp-metadata.xml',
'../../examples/data/idp-public-key.pem',
'../../examples/data/idp-private-key.pem',
'../../examples/data/idp-crt.pem',
lasso.signatureMethodRsaSha1)
- server.add_provider(
+ lassoServer.add_provider(
'../../examples/data/sp-metadata.xml',
'../../examples/data/sp-public-key.pem',
'../../examples/data/ca-crt.pem')
- site.serverDump = server.dump()
- failUnless(site.serverDump)
- server.destroy()
-
- site.addWebUser('Chantereau')
- site.addWebUser('Clapies')
- site.addWebUser('Febvre')
- site.addWebUser('Nowicki')
+ site.lassoServerDump = lassoServer.dump()
+ failUnless(site.lassoServerDump)
+ lassoServer.destroy()
+
+ site.addUser('Chantereau')
+ site.addUser('Clapies')
+ site.addUser('Febvre')
+ site.addUser('Nowicki')
# Frederic Peters has no account on identity provider.
return site
def generateLibertyEnabledClient(self, internet):
client = LibertyEnabledClient(internet)
- # FIXME: Lasso should provide a way for Liberty-enabled client to create a "server" without
- # metadata, instead of using 'singleSignOnServiceUrl'.
-## server = lasso.Server.new(
-## None, # A LECP has no metadata.
-## '../../examples/data/client-public-key.pem',
-## '../../examples/data/client-private-key.pem',
-## '../../examples/data/client-crt.pem',
-## lasso.signatureMethodRsaSha1)
-## server.add_provider(
-## '../../examples/data/idp-metadata.xml',
-## '../../examples/data/idp-public-key.pem',
-## '../../examples/data/ca-crt.pem')
-## client.server = server
- client.idpSingleSignOnServiceUrl = 'https://identity-provider/singleSignOn'
+ lassoServer = lasso.Server.new()
+ lassoServer.add_provider(
+ '../../examples/data/idp-metadata.xml',
+ '../../examples/data/idp-public-key.pem',
+ '../../examples/data/ca-crt.pem')
+ client.lassoServerDump = lassoServer.dump()
+ failUnless(client.lassoServerDump)
+ lassoServer.destroy()
return client
def generateSpSite(self, internet):
site = ServiceProvider(internet, 'https://service-provider/')
site.providerId = 'https://service-provider/metadata'
- server = lasso.Server.new(
+ lassoServer = lasso.Server.new(
'../../examples/data/sp-metadata.xml',
'../../examples/data/sp-public-key.pem',
'../../examples/data/sp-private-key.pem',
'../../examples/data/sp-crt.pem',
lasso.signatureMethodRsaSha1)
- server.add_provider(
+ lassoServer.add_provider(
'../../examples/data/idp-metadata.xml',
'../../examples/data/idp-public-key.pem',
'../../examples/data/ca-crt.pem')
- site.serverDump = server.dump()
- failUnless(site.serverDump)
- server.destroy()
+ site.lassoServerDump = lassoServer.dump()
+ failUnless(site.lassoServerDump)
+ lassoServer.destroy()
- site.addWebUser('Nicolas')
- site.addWebUser('Romain')
- site.addWebUser('Valery')
+ site.addUser('Nicolas')
+ site.addUser('Romain')
+ site.addUser('Valery')
# Christophe Nowicki has no account on service provider.
- site.addWebUser('Frederic')
+ site.addUser('Frederic')
return site
def setUp(self):
@@ -120,19 +114,6 @@ class LoginTestCase(unittest.TestCase):
'failUnlessAlmostEqual', 'failUnlessRaises', 'failUnlessEqual'):
builtins.delete(name)
- def test00(self):
- """LECP login."""
- internet = Internet()
- idpSite = self.generateIdpSite(internet)
- spSite = self.generateSpSite(internet)
- spSite.idpSite = idpSite
- lec = self.generateLibertyEnabledClient(internet)
- principal = Principal(internet, 'Romain Chantereau')
- principal.keyring[idpSite.url] = 'Chantereau'
- principal.keyring[spSite.url] = 'Romain'
- httpResponse = lec.login(principal, spSite, '/login')
- raise str((httpResponse.headers['Content-Type'], httpResponse.body))
-
def test01(self):
"""Service provider initiated login using HTTP redirect and service provider initiated logout using SOAP."""
@@ -148,8 +129,8 @@ class LoginTestCase(unittest.TestCase):
failUnlessEqual(httpResponse.statusCode, 200)
httpResponse = principal.sendHttpRequestToSite(spSite, 'GET', '/logoutUsingSoap')
failUnlessEqual(httpResponse.statusCode, 200)
- failIf(spSite.webSessions)
- failIf(idpSite.webSessions)
+ failIf(spSite.sessions)
+ failIf(idpSite.sessions)
def test02(self):
"""Service provider initiated login using HTTP redirect and service provider initiated logout using SOAP. Done three times."""
@@ -264,20 +245,19 @@ class LoginTestCase(unittest.TestCase):
httpResponse = principal.sendHttpRequestToSite(spSite, 'GET', '/logoutUsingSoap')
failUnlessEqual(httpResponse.statusCode, 401)
-## def test06(self):
-## """Service provider LECP login."""
-
-## # LECP has asked service provider for login.
-## spServer = self.getServer()
-
-## # FIXME: Why doesn't lasso.Lecp.new have spServer as argument?
-## # spLecp = lasso.Lecp.new(spServer)
-## spLecp = lasso.Lecp.new()
-## spLecp.init_authn_request_envelope(sp, )
-## lasso_lecp_init_authn_request_envelope(sp_lecp, spserver, authnRequest);
-## lasso_lecp_build_authn_request_envelope_msg(sp_lecp);
-## msg = g_strdup(sp_lecp->msg_body);
-## lasso_lecp_destroy(sp_lecp);
+ def test07(self):
+ """LECP login."""
+ internet = Internet()
+ idpSite = self.generateIdpSite(internet)
+ spSite = self.generateSpSite(internet)
+ spSite.idpSite = idpSite
+ lec = self.generateLibertyEnabledClient(internet)
+ lec.idpSite = idpSite
+ principal = Principal(internet, 'Romain Chantereau')
+ principal.keyring[idpSite.url] = 'Chantereau'
+ principal.keyring[spSite.url] = 'Romain'
+ httpResponse = lec.login(principal, spSite, '/login')
+ raise str((httpResponse.statusCode, httpResponse.statusMessage, httpResponse.headers['Content-Type'], httpResponse.body))
suite1 = unittest.makeSuite(LoginTestCase, 'test')
diff --git a/python/tests/websimulator.py b/python/tests/websimulator.py
index 0c923d05..411a1268 100644
--- a/python/tests/websimulator.py
+++ b/python/tests/websimulator.py
@@ -23,7 +23,7 @@
# FIXME: Replace principal with client in most methods.
-# FIXME: Rename webUser to userAccount.
+# FIXME: Rename user to userAccount.
import abstractweb
@@ -48,11 +48,9 @@ class HttpRequest(abstractweb.HttpRequestMixin, object):
if form:
self.form = form
- def getFormField(self, name, default = 'none'):
- if self.form and name in self.form:
- return self.form[name]
- if default == 'none':
- raise KeyError(name)
+ def getFormField(self, name, default = None):
+ if self.form is not None:
+ return self.form.get(name, default)
return default
def getPath(self):
@@ -76,6 +74,11 @@ class HttpRequest(abstractweb.HttpRequestMixin, object):
def getScheme(self):
return self.url.split(':', 1)[0].lower()
+ def hasFormField(self, name):
+ if self.form is None:
+ return False
+ return name in self.form
+
def send(self):
webSite = self.client.internet.getWebSite(self.url)
return webSite.handleHttpRequest(self)
@@ -94,13 +97,18 @@ class HttpResponse(abstractweb.HttpResponseMixin, object):
class HttpRequestHandler(abstractweb.HttpRequestHandlerMixin, object):
HttpResponse = HttpResponse # Class
- def __init__(self, webServer, httpRequest):
- self.webServer = webServer
+ def __init__(self, site, httpRequest):
+ self.site = site
self.httpRequest = httpRequest
+ def getSession(self):
+ return self.site.getSessionFromPrincipal(self.httpRequest.client)
+
def respondRedirectTemporarily(self, url):
return self.httpRequest.client.redirect(url)
+ session = property(getSession)
+
class Internet(object):
webSites = None
@@ -125,13 +133,13 @@ class WebClient(object):
'User-Agent': 'LassoSimulator/0.0.0',
'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html',
}
- webSessionIds = None # Simulate the cookies, stored in user's navigator, and containing the
+ sessionTokens = None # Simulate the cookies, stored in user's navigator, and containing the
# IDs of sessions already opened by the user.
def __init__(self, internet):
self.internet = internet
self.keyring = {}
- self.webSessionIds = {}
+ self.sessionTokens = {}
def redirect(self, url):
return self.sendHttpRequest('GET', url)
@@ -169,104 +177,99 @@ class Principal(WebClient):
self.name = name
-class WebSession(object):
+class WebSession(abstractweb.WebSessionMixin, object):
"""Simulation of session of a web site"""
expirationTime = None # A sample session variable
+ isDirty = True
loginDump = None # Used only by some identity providers
- uniqueId = None # The session number
- sessionDump = None
- webUserId = None # ID of logged user.
-
- def __init__(self, uniqueId):
- self.uniqueId = uniqueId
-
-
-class WebUser(object):
- """Simulation of user of a web site"""
+ lassoSessionDump = None
+ userId = None # ID of logged user.
- identityDump = None
- language = 'fr' # A sample user variable
- uniqueId = None # The user name is used as an ID in this simulation.
+ def __init__(self, token):
+ self.token = token
- def __init__(self, uniqueId):
- self.uniqueId = uniqueId
+ def save(self):
+ pass
-class WebSite(WebClient):
+class WebSite(abstractweb.WebSiteMixin, WebClient):
"""Simulation of a web site"""
httpResponseHeaders = {
'Server': 'Lasso Simulator Web Server',
}
- lastWebSessionId = 0
+ lastSessionToken = 0
providerId = None # The Liberty providerID of this web site
url = None # The main URL of web site
- webUsers = None
- webSessions = None
+ users = None
+ sessions = None
def __init__(self, internet, url):
WebClient.__init__(self, internet)
self.url = url
- self.webUserIdsByNameIdentifier = {}
- self.webUsers = {}
- self.webSessionIdsByNameIdentifier = {}
- self.webSessions = {}
+ self.userIdsByNameIdentifier = {}
+ self.users = {}
+ self.sessionTokensByNameIdentifier = {}
+ self.sessions = {}
self.internet.addWebSite(self)
- def addWebUser(self, name):
- self.webUsers[name] = WebUser(name)
+ def addUser(self, name):
+ self.users[name] = WebUser(name)
- def createWebSession(self, client):
- self.lastWebSessionId += 1
- webSession = WebSession(self.lastWebSessionId)
- self.webSessions[self.lastWebSessionId] = webSession
- client.webSessionIds[self.url] = self.lastWebSessionId
- return webSession
+ def createSession(self, client):
+ self.lastSessionToken += 1
+ session = WebSession(self.lastSessionToken)
+ self.sessions[self.lastSessionToken] = session
+ client.sessionTokens[self.url] = self.lastSessionToken
+ return session
def getIdentityDump(self, principal):
- webSession = self.getWebSession(principal)
- webUser = self.getWebUserFromWebSession(webSession)
- if webUser is None:
- return None
- return webUser.identityDump
-
- def getSessionDump(self, principal):
- webSession = self.getWebSession(principal)
- if webSession is None:
+ session = self.getSessionFromPrincipal(principal)
+ user = self.getUserFromSession(session)
+ if user is None:
return None
- return webSession.sessionDump
+ return user.lassoIdentityDump
- def getWebSession(self, principal):
- webSessionId = principal.webSessionIds.get(self.url, None)
- if webSessionId is None:
- # The user has no web session opened on this site.
+ def getLassoSessionDump(self, principal):
+ session = self.getSessionFromPrincipal(principal)
+ if session is None:
return None
- return self.webSessions.get(webSessionId, None)
+ return session.lassoSessionDump
- def getWebSessionFromNameIdentifier(self, nameIdentifier):
- webSessionId = self.webSessionIdsByNameIdentifier.get(nameIdentifier, None)
- if webSessionId is None:
+ def getSessionFromNameIdentifier(self, nameIdentifier):
+ sessionToken = self.sessionTokensByNameIdentifier.get(nameIdentifier, None)
+ if sessionToken is None:
# The user has no federation on this site or has no authentication assertion for this
# federation.
return None
- return self.webSessions.get(webSessionId, None)
+ return self.sessions.get(sessionToken, None)
+
+ def getSessionFromPrincipal(self, principal):
+ sessionToken = principal.sessionTokens.get(self.url, None)
+ return self.getSessionFromToken(sessionToken)
- def getWebUserFromNameIdentifier(self, nameIdentifier):
- webUserId = self.webUserIdsByNameIdentifier.get(nameIdentifier, None)
- if webUserId is None:
+ def getSessionFromToken(self, sessionToken):
+ if sessionToken is None:
+ # The user has no web session opened on this site.
+ return None
+ return self.sessions.get(sessionToken, None)
+
+ def getUserFromNameIdentifier(self, nameIdentifier):
+ userId = self.userIdsByNameIdentifier.get(nameIdentifier, None)
+ if userId is None:
# The user has no federation on this site.
return None
- return self.webUsers.get(webUserId, None)
+ return self.users.get(userId, None)
- def getWebUserFromWebSession(self, webSession):
- if webSession is None:
+ def getUserFromSession(self, session):
+ if session is None:
return None
- webUserId = webSession.webUserId
- if webUserId is None:
+ userId = session.userId
+ if userId is None:
# The user has no account on this site.
return None
- return self.webUsers.get(webUserId, None)
+ return self.users.get(userId, None)
def handleHttpRequest(self, httpRequest):
httpRequestHandler = HttpRequestHandler(self, httpRequest)
@@ -280,3 +283,14 @@ class WebSite(WebClient):
return httpRequestHandler.respond(
404, 'Path "%s" Not Found.' % httpRequestHandler.httpRequest.path)
return method(httpRequestHandler)
+
+
+class WebUser(abstractweb.WebUserMixin, object):
+ """Simulation of user of a web site"""
+
+ lassoIdentityDump = None
+ language = 'fr' # A sample user variable
+ uniqueId = None # The user name is used as an ID in this simulation.
+
+ def __init__(self, uniqueId):
+ self.uniqueId = uniqueId