Liberty Alliance login now works (but may be a raise is not a clean way to

show that it works).
This commit is contained in:
eraviart 2004-07-19 22:34:24 +00:00
parent 7db3f1fa4a
commit a24143c55e
13 changed files with 231 additions and 78 deletions

View File

@ -25,6 +25,8 @@
"""Inscriptions Module"""
import os
import libxml2
import dataholders
@ -367,7 +369,10 @@ class InscriptionHolder(dataholders.XmlHolder):
return None
# We replace " " by "-" instead of "_" because this is better for Google
# indexing.
return strings.simplify(email)
publicName = strings.simplify(email)
if self.isRootElder:
publicName = os.path.join(self.getDirectoryAbsolutePath(), publicName)
return publicName
def setPublicName(self, publicName):
pass

View File

@ -808,7 +808,10 @@ class XmlHolder(DataHolder, documents.AbstractDocument):
else:
doc = self.parseDataFile()
self._node = doc
self.publicName = self.localId
if self.isRootElder:
self.publicName = self.getAbsolutePath()
else:
self.publicName = self.localId
doc.xincludeProcess()
# Now that we know what is the root node, we can change the class of the holder.
@ -901,7 +904,12 @@ class XmlHolder(DataHolder, documents.AbstractDocument):
fileSystem = self.fileSystem
fileSystemPath = self.getFileSystemPath()
if isRenamed:
fileSystem.rename(oldFileSystemPath, fileSystemPath)
try:
fileSystem.rename(oldFileSystemPath, fileSystemPath)
except OSError:
logs.debug('oldFileSystemPath = "%s", fileSystemPath = "%s"' % (
oldFileSystemPath, fileSystemPath))
raise
dataFile = fileSystem.open(fileSystemPath, "wb")
dataFile.write(data)
dataFile.close()

View File

@ -318,7 +318,6 @@ class HttpRequestHandlerMixin:
message = """Access to "%s" Forbidden.""" % filePath
if station == "current":
station = environs.getVar("currentStation", default = None)
self.setCookie()
logs.info(message)
data = "<html><body>%s</body></html>" % message
if station:
@ -331,7 +330,7 @@ class HttpRequestHandlerMixin:
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
return self.send_error(403, message, data)
return self.send_error(403, message, data, setCookie = True)
def outputErrorBadRequest(self, reason, station = "current"):
if reason:
@ -393,7 +392,6 @@ class HttpRequestHandlerMixin:
message = """Path "%s" Not Found.""" % filePath
if station == "current":
station = environs.getVar("currentStation", default = None)
self.setCookie()
logs.info(message)
data = "<html><body>%s</body></html>" % message
if station:
@ -406,7 +404,7 @@ class HttpRequestHandlerMixin:
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
return self.send_error(404, message, data)
return self.send_error(404, message, data, setCookie = True)
def outputErrorUnauthorized(self, filePath, station = "current"):
if filePath is None:
@ -415,7 +413,6 @@ class HttpRequestHandlerMixin:
message = """Access to "%s" Unauthorized.""" % filePath
if station == "current":
station = environs.getVar("currentStation", default = None)
self.setCookie()
logs.info(message)
data = "<html><body>%s</body></html>" % message
headers = {}
@ -432,7 +429,7 @@ class HttpRequestHandlerMixin:
if station.getConfigBoolean("yep:useHttpAuthentication", default = False):
headers["WWW-Authenticate"] = 'Basic realm="%s"' % station.getConfigString(
"yep:title", default = "Expression")
return self.send_error(401, message, data, headers)
return self.send_error(401, message, data, headers, setCookie = True)
def outputInformationContinue(self):
message = "Continue"
@ -584,7 +581,7 @@ Client: %(clientIp)s
return self.outputErrorInternalServer()
def send_error(self, code, message = None, data = None, headers = None):
def send_error(self, code, message = None, data = None, headers = None, setCookie = False):
# We need to save the session before sending response, otherwise, the
# server may receive a new HTTP request before the session is saved.
session = environs.getVar("session", default = None)
@ -605,6 +602,8 @@ Client: %(clientIp)s
if headers is not None:
for name, value in headers.items():
self.send_header(name, value)
if setCookie:
self.setCookie()
self.end_headers()
if self.command != "HEAD":
self.wfile.write(data)

View File

@ -251,6 +251,7 @@ class Application(object):
if submission.hasField("login") and not authorization \
and rootDataHolder.getConfigBoolean("yep:useHttpAuthentication",
default = False):
# Ask for HTTP authentication.
return httpRequestHandler.outputErrorUnauthorized(httpPath)
if submission.hasField("logout") and authorization:
# Since HTTP authentication provides no way to logout, we send a status

View File

@ -25,6 +25,7 @@
"""Sessions Module"""
import errno
import os
import socket

View File

@ -358,7 +358,9 @@ class DbXmlDocumentStation(stations.AbstractStation, documents.AbstractDocument)
def getPublicName(self):
publicName = self.node.prop("publicName")
if publicName is None:
return ""
return None
if self.isRootElder:
publicName = os.path.join(self.getDirectoryAbsolutePath(), publicName)
return publicName
def outputHttpSource(self):

View File

@ -25,12 +25,18 @@
"""Identities Module"""
import errno
import libxml2
import expression.core.dataholders as dataholders
import expression.core.directories as directories
import expression.core.elements as elements
import expression.core.faults as faults
import expression.core.filesystems as filesystems
import expression.core.logs as logs
import expression.core.namespaces as namespaces
import expression.core.sessions as sessions
import expression.core.stations as stations
import expression.core.things as things
@ -38,8 +44,17 @@ from persons import Person
class Identity(things.Thing):
_libertyAllianceAccount = None
_session = None
def deleteLibertyAllianceAccountPath(self):
nodes = self.evaluateXpath("yep:libertyAllianceAccount")
if nodes:
node = nodes[0]
node.unlinkNode()
node.freeNode()
self.getDataHolder().isDirty = True
def deleteSessionToken(self):
nodes = self.evaluateXpath("yep:sessionToken")
if nodes:
@ -64,6 +79,44 @@ class Identity(things.Thing):
person = personHolder.getRootElement()
return person.getLanguage()
def getLibertyAllianceAccount(self):
if self._libertyAllianceAccount is None:
libertyAllianceAccountAbsolutePath = self.getLibertyAllianceAccountAbsolutePath()
if libertyAllianceAccountAbsolutePath:
try:
libertyAllianceAccountHolder = dataholders.DataHolder(
pathFragment = libertyAllianceAccountAbsolutePath, mimeType = "text/xml",
isRootElder = True,
containedFileSystem = filesystems.PartialFileSystem(
libertyAllianceAccountAbsolutePath))
except IOError, error:
if error.errno == errno.ENOENT:
logs.debug("""LibertyAllianceAccount at path "%s" doesn't exist."""
% libertyAllianceAccountAbsolutePath)
self._libertyAllianceAccount = "none"
else:
raise
else:
self._libertyAllianceAccount = libertyAllianceAccountHolder.getRootElement()
else:
libertyAllianceAccountNodes = self.evaluateXpath("yep:libertyAllianceAccount")
if libertyAllianceAccountNodes:
self._libertyAllianceAccount = elements.newElement(
libertyAllianceAccountNodes[0], previous = self, owner = self)
else:
self._libertyAllianceAccount = "none"
if self._libertyAllianceAccount == "none":
return None
return self._libertyAllianceAccount
def getLibertyAllianceAccountAbsolutePath(self):
nodes = self.evaluateXpath("yep:libertyAllianceAccount/@src")
if nodes:
libertyAllianceAccountPath = nodes[0].content
return self.convertPathToAbsolute(libertyAllianceAccountPath)
else:
return None
def getPerson(self):
if self.getPersonLocation():
personHolder = self.walkToLocation(self.getPersonLocation())
@ -88,7 +141,7 @@ class Identity(things.Thing):
if sessionToken is None:
self._session = "none"
else:
self._session = sessions.retrieveSession(getSessionToken)
self._session = sessions.retrieveSession(sessionToken)
if self._session == "none":
return None
return self._session
@ -106,6 +159,12 @@ class Identity(things.Thing):
else:
return person.getSimpleLabel()
def getUser(self):
# Some times, users are stored in independant files; some times they are embedded inside
# accounts.
# => This method is needed to ease user retrieval.
return self
def getVoteToken(self, ballotBoxLocalId):
votesNodes = self.evaluateXpath("yep:votes")
if not votesNodes:
@ -120,6 +179,12 @@ class Identity(things.Thing):
return None
return tokenNodes[0].content
def setLibertyAllianceAccountAbsolutePath(self, libertyAllianceAccountAbsolutePath):
libertyAllianceAccountBaseRelativePath = self.convertAbsolutePathToBaseRelative(
libertyAllianceAccountAbsolutePath)
self.setElementAttribute(
"yep:libertyAllianceAccount", "src", libertyAllianceAccountBaseRelativePath)
def setPersonLocation(self, personLocation):
self.setElementAttribute("yep:person", "src", personLocation)

View File

@ -142,7 +142,7 @@ class LibertyAlliance(things.Thing):
authenticationMethod = session.getAuthenticationMethod()
if authenticationMethod == "password":
authenticationMethod = lasso.samlAuthenticationMethodPassword
lassoAuthenticationMethod = lasso.samlAuthenticationMethodPassword
else:
raise Exception("Unknown authentication method %s" % authenticationMethod)
@ -207,7 +207,7 @@ class LibertyAlliance(things.Thing):
idpNameIdentifierFormat = "onetime"
authenticationStatement = lasso.AuthenticationStatement(
authenticationMethod,
lassoAuthenticationMethod,
"2005-05-03T16:12:00Z", # reauthenticateOnOrAfter
nameIdentifier = nameIdentifier,
nameQualifier = serviceProviderId,
@ -369,20 +369,22 @@ You have been succesfully authenticated; click ok to go back to the service prov
account = accountHolder.getRootElement()
account.setNameIdentifier(nameIdentifier)
account.setUserAbsolutePath(user.getAbsolutePath())
user.setLibertyAllianceAccountAbsolutePath(account.getAbsolutePath())
userContextDump = None
else:
account = accountHolder.getRootElement()
user = account.getUser()
userContextDump = account.getUserContextDump()
loginContext.create_user(userContextDump)
# Save the new or updated userContext into account.
account.setUserContextDump(loginContext.user.dump())
# Retrieve the authentication method and store it in account.
authenticationMethod = loginContext.response.get_child(
lassoAuthenticationMethod = loginContext.response.get_child(
"AuthenticationStatement").get_attr_value("AuthenticationMethod")
if authenticationMethod == lasso.samlAuthenticationMethodPassword:
if lassoAuthenticationMethod == lasso.samlAuthenticationMethodPassword:
authenticationMethod = "password"
else:
logs.debug('Unknown authentication method = "%s"' % authenticationMethod)
logs.debug('Unknown authentication method = "%s"' % lassoAuthenticationMethod)
account.setAuthenticationMethod(authenticationMethod)
account.getDocument().save()
# User is now authenticated => create session, cookie...
@ -595,9 +597,10 @@ You have been succesfully authenticated; click ok to go back to the service prov
serverContextDump = self.getServerContextDump()
serverContext = lasso.Server.new_from_dump(serverContextDump)
userContext = None
account = None
user = environs.getVar("user")
if user is not None:
account = user.getAccount()
account = user.getLibertyAllianceAccount()
if account is not None:
userContextDump = account.getUserContextDump()
if userContextDump is not None:
@ -616,61 +619,110 @@ You have been succesfully authenticated; click ok to go back to the service prov
mustAuthenticate = loginContext.must_authenticate()
logs.debug("User must be authenticated = %s" % mustAuthenticate)
if mustAuthenticate:
# FIXME: Authenticate the user and set the right authenticationMethod.
userAuthenticated = True
authenticationMethod = lasso.samlAuthenticationMethodPassword
if loginContext.protocolProfile == lasso.loginProtocolProfileBrwsArt:
# We can send the artifact either using a redirect or a POST. It is an IDP choice,
# but redirect is better than POST (no need for a page with a form).
useRedirectForArtifact = True
if useRedirectForArtifact:
errorCode = loginContext.build_artifact_msg(
userAuthenticated, authenticationMethod, "FIXME: reauthenticateOnOrAfter",
lasso.httpMethodRedirect)
if errorCode:
raise Exception("Lasso login error %s" % errorCode)
logs.debug("msg_url = %s; assertionArtifact = %s; response_dump = %s" % (
loginContext.msg_url, loginContext.assertionArtifact,
loginContext.response_dump))
# Save response dump, to retrieve it later, when receiving a request with the
# same artifact.
responsesPath = self.getResponsesDirectoryAbsolutePath()
responsesHolder = directories.DirectoryHolder(
pathFragment = responsesPath, previous = environs.getVar("rootStation"),
isRootElder = True,
containedFileSystem = filesystems.PartialFileSystem(responsesPath))
responseFeature = modules.getElementFeature(
namespaces.yep.uri, "libertyAllianceResponse")
responseHolder = responseFeature.newXmlHolder(
responsesHolder, pathFragment = loginContext.assertionArtifact + ".xml")
response = responseHolder.getRootElement()
response.setArtifact(loginContext.assertionArtifact)
response.setResponseDump(loginContext.response_dump)
response.getDocument().save()
return environs.getVar("httpRequestHandler").outputRedirect(
loginContext.msg_url)
if self.getConfigBoolean("yep:useHttpAuthentication", default = False):
# HTTP authentication.
if user is None:
return environs.getVar("httpRequestHandler").outputErrorUnauthorized(
self.getUriAbsolutePath())
else:
errorCode = loginContext.build_artifact_msg(
userAuthenticated, authenticationMethod, "FIXME: reauthenticateOnOrAfter",
lasso.httpMethodPost)
if errorCode:
raise Exception("Lasso login error %s" % errorCode)
logs.debug("msg_url = %s; msg_body = %s; msg_relayState = %s; "
"assertionArtifact = %s; response_dump = %s" % (
loginContext.msg_url, loginContext.msg_body, loginContext.msg_relayState,
loginContext.assertionArtifact, loginContext.response_dump))
raise "FIXME"
else: # loginContext.protocolProfile == lasso.loginProtocolProfileBrwsPost:
errorCode = loginContext.build_authn_response_msg(
userAuthenticated, authenticationMethod, "FIXME: reauthenticateOnOrAfter")
# The user is already authenticated using HTTP authentication.
userAuthenticated = True
authenticationMethod = "password"
else:
# FIXME: Authenticate the user and set the right authenticationMethod.
raise "TODO"
else:
userAuthenticated = True
authenticationMethod = "password" # FIXME
if authenticationMethod == "password":
lassoAuthenticationMethod = lasso.samlAuthenticationMethodPassword
else:
logs.debug('Unknown authentication method = "%s"' % authenticationMethod)
if loginContext.protocolProfile == lasso.loginProtocolProfileBrwsArt:
# We can send the artifact either using a redirect or a POST. It is an IDP choice,
# but redirect is better than POST (no need for a page with a form).
useRedirectForArtifact = True
if useRedirectForArtifact:
errorCode = loginContext.build_artifact_msg(
userAuthenticated, lassoAuthenticationMethod,
"FIXME: reauthenticateOnOrAfter", lasso.httpMethodRedirect)
if errorCode:
raise Exception("Lasso login error %s" % errorCode)
logs.debug("msg_url = %s; msg_body = %s; msg_relayState = %s" % (
loginContext.msg_url, loginContext.msg_body, loginContext.msg_relayState))
logs.debug("msg_url = %s; assertionArtifact = %s; response_dump = %s" % (
loginContext.msg_url, loginContext.assertionArtifact,
loginContext.response_dump))
nameIdentifier = loginContext.nameIdentifier
# Save user dump and name identifier.
if account is None:
accountsPath = self.getAccountsDirectoryAbsolutePath()
accountsHolder = directories.DirectoryHolder(
pathFragment = accountsPath, previous = environs.getVar("rootStation"),
isRootElder = True,
containedFileSystem = filesystems.PartialFileSystem(accountsPath))
accountLocalId = accountsHolder.getItemLocalId(nameIdentifier)
if accountLocalId is None:
accountHolder = None
else:
accountHolder = accountsHolder.getItem(accountLocalId)
if accountHolder is None:
# There is no account indexed by this name identifier. Create it and
# link it to the user.
accountFeature = modules.getElementFeature(
namespaces.yep.uri, "libertyAllianceAccount")
accountHolder = accountFeature.newXmlHolder(
accountsHolder, pathFragment = nameIdentifier + ".xml")
account = accountHolder.getRootElement()
account.setNameIdentifier(nameIdentifier)
else:
account = accountHolder.getRootElement()
logs.warning(
'Liberty Alliance account "%s" was not referenced by user %s'
% (account.getAbsolutePath(), user.getAbsolutePath()))
account.setUserAbsolutePath(user.getAbsolutePath())
user.setLibertyAllianceAccountAbsolutePath(account.getAbsolutePath())
user.getDocument().save()
# Save the new or updated userContext into account.
account.setUserContextDump(loginContext.user.dump())
account.setAuthenticationMethod(authenticationMethod)
account.getDocument().save()
# Save response dump, to retrieve it later, when receiving a request with the
# same artifact.
responsesPath = self.getResponsesDirectoryAbsolutePath()
responsesHolder = directories.DirectoryHolder(
pathFragment = responsesPath, previous = environs.getVar("rootStation"),
isRootElder = True,
containedFileSystem = filesystems.PartialFileSystem(responsesPath))
responseFeature = modules.getElementFeature(
namespaces.yep.uri, "libertyAllianceResponse")
responseHolder = responseFeature.newXmlHolder(
responsesHolder, pathFragment = loginContext.assertionArtifact + ".xml")
response = responseHolder.getRootElement()
response.setArtifact(loginContext.assertionArtifact)
response.setResponseDump(loginContext.response_dump)
response.getDocument().save()
return environs.getVar("httpRequestHandler").outputRedirect(
loginContext.msg_url)
else:
errorCode = loginContext.build_artifact_msg(
userAuthenticated, lassoAuthenticationMethod,
"FIXME: reauthenticateOnOrAfter", lasso.httpMethodPost)
if errorCode:
raise Exception("Lasso login error %s" % errorCode)
logs.debug("msg_url = %s; msg_body = %s; msg_relayState = %s; "
"assertionArtifact = %s; response_dump = %s" % (
loginContext.msg_url, loginContext.msg_body, loginContext.msg_relayState,
loginContext.assertionArtifact, loginContext.response_dump))
raise "FIXME"
else:
else: # loginContext.protocolProfile == lasso.loginProtocolProfileBrwsPost:
errorCode = loginContext.build_authn_response_msg(
userAuthenticated, lassoAuthenticationMethod, "FIXME: reauthenticateOnOrAfter")
if errorCode:
raise Exception("Lasso login error %s" % errorCode)
logs.debug("msg_url = %s; msg_body = %s; msg_relayState = %s" % (
loginContext.msg_url, loginContext.msg_body, loginContext.msg_relayState))
raise "FIXME"
raise "TODO"

View File

@ -26,9 +26,11 @@
import errno
import os
import expression.core.dataholders as dataholders
import expression.core.elements as elements
import expression.core.filesystems as filesystems
import expression.core.namespaces as namespaces
import expression.core.things as things
@ -99,7 +101,10 @@ class LibertyAllianceAccount(things.Thing):
else:
raise
else:
self._user = userHolder.getRootElement()
# Some times, users are stored in independant files; some times they are
# embedded inside accounts.
accountOrUser = userHolder.getRootElement()
self._user = accountOrUser.getUser()
else:
userNodes = self.evaluateXpath("yep:user")
if userNodes:
@ -144,10 +149,12 @@ class LibertyAllianceAccountHolder(dataholders.XmlHolder):
pass
def getPublicName(self):
nameIdentifier = self.getRootElement().getNameIdentifier()
if nameIdentifier is None:
publicName = self.getRootElement().getNameIdentifier()
if publicName is None:
return None
return nameIdentifier
if self.isRootElder:
publicName = os.path.join(self.getDirectoryAbsolutePath(), publicName)
return publicName
def setPublicName(self, publicName):
pass

View File

@ -25,6 +25,8 @@
"""Liberty Alliance Responses Module"""
import os
import expression.core.dataholders as dataholders
import expression.core.elements as elements
import expression.core.namespaces as namespaces
@ -72,10 +74,12 @@ class LibertyAllianceResponseHolder(dataholders.XmlHolder):
pass
def getPublicName(self):
artifact = self.getRootElement().getArtifact()
if artifact is None:
publicName = self.getRootElement().getArtifact()
if publicName is None:
return None
return artifact
if self.isRootElder:
publicName = os.path.join(self.getDirectoryAbsolutePath(), publicName)
return publicName
def setPublicName(self, publicName):
pass

View File

@ -26,6 +26,7 @@
import errno
import os
import expression.core.dataholders as dataholders
import expression.core.directories as directories
@ -246,7 +247,10 @@ class PasswordAccount(things.Thing):
else:
raise
else:
self._user = userHolder.getRootElement()
# Some times, users are stored in independant files; some times they are
# embedded inside accounts.
accountOrUser = userHolder.getRootElement()
self._user = accountOrUser.getUser()
else:
userNodes = self.evaluateXpath("yep:user")
if userNodes:
@ -288,7 +292,10 @@ class PasswordAccountHolder(dataholders.XmlHolder):
email = self.getRootElement().email
if email is None:
return None
return strings.simplify(email)
publicName = strings.simplify(email)
if self.isRootElder:
publicName = os.path.join(self.getDirectoryAbsolutePath(), publicName)
return publicName
def setPublicName(self, publicName):
pass

View File

@ -2,4 +2,5 @@
<configuration xmlns="http://www.entrouvert.org/namespaces/expression/0.0">
<defaultMode>list</defaultMode> <!-- for directories and Zips and... -->
<sessionsPath>sessions</sessionsPath>
<useHttpAuthentication>true</useHttpAuthentication>
</configuration>

View File

@ -7,6 +7,7 @@
<peerPublicKeyFile>../security/public/service-provider-liberty-alliance.key</peerPublicKeyFile>
<peerCACertificateFile>../security/public/ca-liberty-alliance.crt</peerCACertificateFile>
<accountsDirectory>accounts</accountsDirectory>
<responsesDirectory>responses</responsesDirectory>
<mode name="afterAuthentication"><users commands="ACCESS GET"><everybody/></users></mode>