diff --git a/config.in b/config.in index 72c65c3c..18b92ae5 100644 --- a/config.in +++ b/config.in @@ -59,11 +59,11 @@ ServerPort = %(port)s + 3 [AtomsServer] ServerPort = %(port)s + 4 -[AuthenticationServer] -ServerPort = %(port)s + 5 +#authentication# [AuthenticationServer] +#authentication# ServerPort = %(port)s + 5 -[AuthenticationLoginPasswordServer] -ServerPort = %(port)s + 6 +#authentication# [AuthenticationLoginPasswordServer] +#authentication# ServerPort = %(port)s + 6 #authentication# [AuthenticationLdapServer] #authentication# ServerPort = %(port)s + 7 @@ -137,8 +137,8 @@ ServerPort = %(port)s + 27 [PeopleServer] ServerPort = %(port)s + 30 -[PreferencesServer] -ServerPort = %(port)s + 31 +#obsolete# [PreferencesServer] +#obsolete# ServerPort = %(port)s + 31 [RubricsServer] ServerPort = %(port)s + 32 @@ -164,3 +164,17 @@ ServerPort = %(port)s + 38 [VotesServer] ServerPort = %(port)s + 39 +[PasswordAccountsServer] +ServerPort = %(port)s + 40 + +[IdentitiesServer] +ServerPort = %(port)s + 41 + +[ProvidersServer] +ServerPort = %(port)s + 42 + +[AssertionsServer] +ServerPort = %(port)s + 44 + +[X509AccountsServer] +ServerPort = %(port)s + 45 diff --git a/glasnost-web/code/webhandler.py b/glasnost-web/code/webhandler.py index e7d1e878..d146c35a 100644 --- a/glasnost-web/code/webhandler.py +++ b/glasnost-web/code/webhandler.py @@ -249,7 +249,6 @@ class Application(applications.Application): languageSetInUrl = 0, objectId = None, positionalArguments = None, - preferences = None, readLanguages = None, req = req, sectionLevel = 1, @@ -262,6 +261,7 @@ class Application(applications.Application): templatesDirectoryPath = None, templateFileName = None, templatePrefix = None, + user = None, userId = '', userToken = '', virtualHost = None, @@ -420,7 +420,7 @@ class Application(applications.Application): except ImportError: raise apache.SERVER_RETURN, apache.HTTP_NOT_IMPLEMENTED try: - xmlPost = xml.dom.minidom.parseString(xmlPost) + xmlPost = xml.dom.minidom.parseString(xmlPostRaw) except: # TODO: tighter check if context.getVar('debug'): raise @@ -429,16 +429,26 @@ class Application(applications.Application): context.setVar('xmlPostRaw', xmlPostRaw) class FakeFieldStorage: list = [] - fieldStorage = FakeFieldStorage + + def keys(self): + return [] + fieldStorage = FakeFieldStorage() fields = {} - for field in fieldStorage.list: - if field.name is not None and not fields.has_key(field.name): - fields[field.name] = fieldStorage[field.name] - elif field.name is not None: - if not type(fields[field.name]) is type([]): - fields[field.name] = [ fields[field.name] ] - fields[field.name].append(fieldStorage[field.name]) + # This code is commented, because it doesn't work when a field has + # several occurences using the same key. In this case, the key is + # listed several times in fieldStorage.list, but + # fieldStorage[field.name] is already a complete list. +## for field in fieldStorage.list: +## if field.name is not None and not fields.has_key(field.name): +## fields[field.name] = fieldStorage[field.name] +## elif field.name is not None: +## if not type(fields[field.name]) is type([]): +## fields[field.name] = [ fields[field.name] ] +## fields[field.name].append(fieldStorage[field.name]) + for key in fieldStorage.keys(): + if key is not None: + fields[key] = fieldStorage[key] fieldNamesToDelete = [] for fieldName, fieldValue in fields.items(): @@ -508,21 +518,24 @@ class Application(applications.Application): context.setVar('sessionTokenInCookie', sessionTokenInCookie) userToken = '' userId = None + user = None if session and session.has_key('userToken') and session['userToken']: userToken = session['userToken'] context.setVar('userToken', userToken) - userId = getProxyForServerRole('authentication').getUserId() + userId = getWebForServerRole('identities').getUserId() if userId: - userToken = session['userToken'] - else: - userToken = None + try: + user = getWeb(userId).getObject(userId) + except faults.MissingItem: + pass + if user is None: + userToken = '' userId = None del session['userToken'] - if session.has_key('userId'): - del session['userId'] session['isDirty'] = 1 - context.setVar('userId', userId) context.setVar('userToken', userToken) + context.setVar('userId', userId) + context.setVar('user', user) if not userToken and req.headers_in.has_key('Authorization'): authorization = req.headers_in['Authorization'] @@ -531,26 +544,15 @@ class Application(applications.Application): raise apache.SERVER_RETURN, apache.HTTP_NOT_ACCEPTABLE # TODO: http auth - - # Handle preferences - preferences = None - if userToken: - try: - preferences = getWebForServerRole( - 'preferences').getPreference() - except faults.UnknownServerId: - pass - else: - context.setVar('preferences', preferences) - # Handle languages. languages = [] if session and session.has_key('lang'): languages = [session['lang']] - elif preferences is not None and preferences.language \ - and preferences.language != 'None': - languages = [preferences.language] - elif req.headers_in.has_key('Accept-Language'): + elif user is not None: + userLanguage = user.getLanguage() + if userLanguage is not None: + languages = [userLanguage] + if not languages and req.headers_in.has_key('Accept-Language'): try: languages = req.headers_in['Accept-Language'] languages = languages.split(',') @@ -559,7 +561,7 @@ class Application(applications.Application): return HTTP_BAD_REQUEST translationsProxy = getProxyForServerRole('translations') languages = [language == 'C' and 'en' or language - for language in languages] + for language in languages] try: possibleLanguages = translationsProxy.getPossibleLanguages() except (faults.UnknownServerId, faults.UnknownDispatcherInId): diff --git a/glasnost-web/index.py b/glasnost-web/index.py index e1b3406a..9ce2c1eb 100644 --- a/glasnost-web/index.py +++ b/glasnost-web/index.py @@ -62,13 +62,13 @@ def index(): ## pass ## else: ## if virtualHostsCount <= 1: -## peopleWeb = getWebForServerRole('people') +## identitiesWeb = getWebForServerRole('identities') ## try: -## peopleCount = peopleWeb.getObjectsCount() +## identitiesCount = identitiesWeb.getObjectsCount() ## except faults.UserAccessDenied: ## pass ## else: -## if peopleCount == 0: +## if identitiesCount == 0: ## pageNamesWeb = getWebForServerRole('pagenames') ## newVirtualHostWizardId = pageNamesWeb.getIdByName( ## 'newVirtualHost') diff --git a/glasnost-web/login.py b/glasnost-web/login.py index 0177ad5d..4efb2b48 100644 --- a/glasnost-web/login.py +++ b/glasnost-web/login.py @@ -43,23 +43,3 @@ __doc__ = """Glasnost Login Python Web Page""" __version__ = '$Revision$'[11:-2] - - -from glasnost.web.tools import * - - - - -def index(access = ''): - authAdmin = getWebForServerRole('authentication').getAdmin() - for authMethod in authAdmin.authenticationMethods or ['login-password']: - authWeb = getWebForServerRole('authentication-%s' % authMethod) - if authWeb: - break - return authWeb.login(access) - - -def logout(): - authWeb = getWebForServerRole('authentication') - return authWeb.logout() - diff --git a/glasnost-web/sessions.py b/glasnost-web/sessions.py index 8b61671c..eff9cac0 100644 --- a/glasnost-web/sessions.py +++ b/glasnost-web/sessions.py @@ -44,6 +44,7 @@ __doc__ = """Glasnost Sessions Page""" __version__ = '$Revision$'[11:-2] + import time import glasnost.common.context as context @@ -53,9 +54,10 @@ import glasnost.common.xhtmlgenerator as X import glasnost.web.calendaring from glasnost.web.tools import * + def index(): - peopleProxy = getProxyForServerRole('people') - if not peopleProxy.isAdmin(): + identitiesProxy = getProxyForServerRole('identities') + if not identitiesProxy.isAdmin(): return accessForbidden() sessionsProxy = getProxyForServerRole('sessions') history = sessionsProxy.getHistory() @@ -76,18 +78,16 @@ def index(): history.sort(lambda x,y: -cmp(x['startTime'], y['startTime'])) for session in [x for x in history if x.has_key('expirationTime')]: tr = X.tr() - tr += X.td( time.strftime('%Y-%m-%d %H:%M', - time.localtime(session['startTime']))) - tr += X.td( time.strftime('%Y-%m-%d %H:%M', - time.localtime(session['expirationTime']))) - try: - if session['userId'] == 0: - raise faults.MissingItem('') - person = peopleProxy.getObject(session['userId']) - tr += X.td( person.getLabel() ) - except faults.MissingItem: + tr += X.td(time.strftime('%Y-%m-%d %H:%M', + time.localtime(session['startTime']))) + tr += X.td(time.strftime('%Y-%m-%d %H:%M', + time.localtime(session['expirationTime']))) + user = context.getVar('user') + if user is None: tr += X.td() - tr += X.td( session['ipAddress'] ) + else: + tr += X.td(user.getLabel()) + tr += X.td(session['ipAddress']) tbody += tr layout += X.h3(_('Previous Sessions')) @@ -105,25 +105,23 @@ def index(): table += tbody for session in [x for x in history if x.has_key('endTime')]: tr = X.tr() - tr += X.td( time.strftime('%Y-%m-%d %H:%M', - time.localtime(session['startTime']))) - tr += X.td( time.strftime('%Y-%m-%d %H:%M', - time.localtime(session['endTime']))) - try: - if session['userId'] == 0: - raise faults.MissingItem('') - person = peopleProxy.getObject(session['userId']) - tr += X.td( person.getLabel() ) - except faults.MissingItem: + tr += X.td(time.strftime('%Y-%m-%d %H:%M', + time.localtime(session['startTime']))) + tr += X.td(time.strftime('%Y-%m-%d %H:%M', + time.localtime(session['endTime']))) + user = context.getVar('user') + if user is None: tr += X.td() - tr += X.td( session['ipAddress'] ) + else: + tr += X.td(user.getLabel()) + tr += X.td(session['ipAddress']) tbody += tr return writePageLayout(layout, _('Sessions History')) def month(year = '', month = ''): - peopleProxy = getProxyForServerRole('people') - if not peopleProxy.isAdmin(): + identitiesProxy = getProxyForServerRole('identities') + if not identitiesProxy.isAdmin(): return accessForbidden() if not year or not month: @@ -137,8 +135,8 @@ def month(year = '', month = ''): if nextMonth == 13: nextMonth, nextYear = 1, nextYear + 1 - timeStart = time.mktime( [year, month, 1] + [0]*6 ) - timeEnd = time.mktime( [nextYear, nextMonth, 1] + [0]*6 ) + timeStart = time.mktime([year, month, 1] + [0] * 6) + timeEnd = time.mktime([nextYear, nextMonth, 1] + [0] * 6) sessionsProxy = getProxyForServerRole('sessions') history = sessionsProxy.getHistory() @@ -160,7 +158,7 @@ def month(year = '', month = ''): sessions = [] for date, number in sessionsDict.items(): s = SessionStat() - s.date = time.mktime( [int(x) for x in date.split('-')] + 6*[0]) + s.date = time.mktime([int(x) for x in date.split('-')] + 6 * [0]) s.number = number if number: sessions.append(s) diff --git a/profiles/basic.xml.in b/profiles/basic.xml.in index 659d3780..269853d9 100644 --- a/profiles/basic.xml.in +++ b/profiles/basic.xml.in @@ -2,12 +2,11 @@ <_description>Basic Functionalities - authentication - authentication-login-password + identities groups pagenames + passwordaccounts people - preferences sessions virtualhosts diff --git a/servers/AppointmentsServer/AppointmentsServer.py b/servers/AppointmentsServer/AppointmentsServer.py index 81593b32..408ae998 100755 --- a/servers/AppointmentsServer/AppointmentsServer.py +++ b/servers/AppointmentsServer/AppointmentsServer.py @@ -86,50 +86,6 @@ class Appointment(ObjectServerMixin, AppointmentCommon): register(Appointment) -def vCalDateToTime(s): - if len(s) == 8: - return time.mktime(time.strptime(s, '%Y%m%d')) - if len(s) == 15: - return time.mktime(time.strptime(s, '%Y%m%dT%H%M%S')) - return None - -def domToObject(domDocument): - # FIXME: the document could have several VEVENT - nodes = domDocument._get_childNodes()[1]._get_childNodes()[-1]._get_childNodes() - attrs = [(x._get_nodeName(), - x._get_childNodes()[0]._get_nodeValue()) for x in nodes] - # yep, I hope it will work. Incredible. - # TODO: something smarter - # attrs now is a list of pairs (key, value) - # ex: [('UID', 'glasnost://projects.entrouvert.be.lan/appointments/1'), - # ('SUMMARY', 'rdv avec Olivier Lattignies et Etienne Saliez'), - # ('URL', 'http://projects.entrouvert.be.lan/appointments/1'), - # ('REVISION', '0'), - # ('DTSTART', '20030210'), - # ('DTEND', '20030210T235900')] - d = {} - for k,v in attrs: - d[k] = unicode(v.encode('latin-1'), 'utf-8').encode('latin-1') - appointment = Appointment() - if d['UID'].startswith('glasnost://'): - appointment.id = d['UID'] - appointment.title = d['SUMMARY'] - if d.has_key('SEQUENCE'): - appointment.version = d['SEQUENCE'] - if d.has_key('DTSTART'): - t = vCalDateToTime(d['DTSTART']) - if t: - appointment.start = t - if d.has_key('DTEND'): - t = vCalDateToTime(d['DTEND']) - if t: - appointment.end = t - if d.has_key('DESCRIPTION'): - s = d['DESCRIPTION'] - s = s.replace('\\n', '\n') - appointment.body = s - return appointment - class AppointmentsServer(AppointmentsCommonMixin, ObjectsServer): useAdminWritersSet = 1 @@ -150,15 +106,15 @@ class AppointmentsServer(AppointmentsCommonMixin, ObjectsServer): if not participantIds or participantIds == ['']: participantIds = [] - authenticationProxy = getProxyForServerRole('authentication') + identitiesProxy = getProxyForServerRole('identities') result = [] for objectId, object in virtualServer.objects.items(): if not (isAdmin - or authenticationProxy.setContainsUser(object.readersSet) + or identitiesProxy.setContainsUser(object.readersSet) or (object.participantsSet - and getProxyForServerRole('authentication' - ).setContainsUser(object.participantsSet))): + and identitiesProxy.setContainsUser( + object.participantsSet))): continue if participantIds and not self.isAskedParticipant( @@ -220,8 +176,8 @@ class AppointmentsServer(AppointmentsCommonMixin, ObjectsServer): def sendNotification(self, virtualServerId, appointment, subject, body): virtualServer = self.getVirtualServer(virtualServerId) mailFrom = virtualServer.adminEmailAddress - people = getSetContainedIds(appointment.participantsSet) - for personId in people: + personIds = getSetContainedIds(appointment.participantsSet) + for personId in personIds: person = getProxyForServerRole('people').getObject(personId) if person.email: mailTo = person.email @@ -279,6 +235,52 @@ class AppointmentsServer(AppointmentsCommonMixin, ObjectsServer): appointmentsServer = AppointmentsServer() +def domToObject(domDocument): + # FIXME: the document could have several VEVENT + nodes = domDocument._get_childNodes()[1]._get_childNodes()[-1]._get_childNodes() + attrs = [(x._get_nodeName(), + x._get_childNodes()[0]._get_nodeValue()) for x in nodes] + # yep, I hope it will work. Incredible. + # TODO: something smarter + # attrs now is a list of pairs (key, value) + # ex: [('UID', 'glasnost://projects.entrouvert.be.lan/appointments/1'), + # ('SUMMARY', 'rdv avec Olivier Lattignies et Etienne Saliez'), + # ('URL', 'http://projects.entrouvert.be.lan/appointments/1'), + # ('REVISION', '0'), + # ('DTSTART', '20030210'), + # ('DTEND', '20030210T235900')] + d = {} + for k,v in attrs: + d[k] = unicode(v.encode('latin-1'), 'utf-8').encode('latin-1') + appointment = Appointment() + if d['UID'].startswith('glasnost://'): + appointment.id = d['UID'] + appointment.title = d['SUMMARY'] + if d.has_key('SEQUENCE'): + appointment.version = d['SEQUENCE'] + if d.has_key('DTSTART'): + t = vCalDateToTime(d['DTSTART']) + if t: + appointment.start = t + if d.has_key('DTEND'): + t = vCalDateToTime(d['DTEND']) + if t: + appointment.end = t + if d.has_key('DESCRIPTION'): + s = d['DESCRIPTION'] + s = s.replace('\\n', '\n') + appointment.body = s + return appointment + + +def vCalDateToTime(s): + if len(s) == 8: + return time.mktime(time.strptime(s, '%Y%m%d')) + if len(s) == 15: + return time.mktime(time.strptime(s, '%Y%m%dT%H%M%S')) + return None + + if __name__ == "__main__": appointmentsServer.launch(applicationName, applicationRole) diff --git a/servers/ArticlesServer/ArticlesServer.py b/servers/ArticlesServer/ArticlesServer.py index 32f84f75..c68583d7 100755 --- a/servers/ArticlesServer/ArticlesServer.py +++ b/servers/ArticlesServer/ArticlesServer.py @@ -133,7 +133,7 @@ class Article(ObjectServerMixin, ArticleCommon): if self.editionTime: del self.editionTime else: - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() if userId: self.lastEditorId = userId self.editionTime = time.time() @@ -203,9 +203,9 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer): object = commonTools.importThing(objectImport) if not self.canAddObject()or ( not self.isAdmin() and not ( - getProxyForServerRole('authentication').setContainsUser( + getProxyForServerRole('identities').setContainsUser( self.getAdminCore().writersSet) - and getProxyForServerRole('authentication').setContainsUser( + and getProxyForServerRole('identities').setContainsUser( object.writersSet))): if not object.canBeCreatedByClient(): raise faults.UserAccessDenied() @@ -213,7 +213,7 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer): object.setAutomaticalSlots() virtualServer.objects[object.id] = object if object.body is not None: - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() if userId: object.lastEditorId = userId object.editionTime = object.modificationTime @@ -315,12 +315,14 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer): possibleAuthorIds = 'everybody' try: possibleReaderIds = getSetContainedIds( - possibleReadersSet, ['people'], raiseWhenUncountable = 1) + possibleReadersSet, ['identities'], + raiseWhenUncountable = 1) except faults.UncountableGroup: possibleReaderIds = 'everybody' try: possibleWriterIds = getSetContainedIds( - possibleWritersSet, ['people'], raiseWhenUncountable = 1) + possibleWritersSet, ['identities'], + raiseWhenUncountable = 1) except faults.UncountableGroup: possibleWriterIds = 'everybody' objectIds = virtualServer.objects.keys() @@ -333,7 +335,7 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer): for objectId in objectIds: object = virtualServer.loadObjectCore(objectId) if not isAdmin and not getProxyForServerRole( - 'authentication').setContainsUser(object.readersSet): + 'identities').setContainsUser(object.readersSet): continue if not self.getLastObjectIds_filter( possibleAuthorIds, 1, object.authorsSet): @@ -495,7 +497,7 @@ class ArticlesServer(ArticlesCommonMixin, ObjectsServer): multiCall = MultiCall() for objectId, object in virtualServer.objects.items(): if not isAdmin \ - and not getProxyForServerRole('authentication' + and not getProxyForServerRole('identities' ).setContainsUser(object.readersSet): continue if 'body' in scope: diff --git a/servers/AssertionsServer/AssertionsServer.py b/servers/AssertionsServer/AssertionsServer.py new file mode 100755 index 00000000..6e7f08e0 --- /dev/null +++ b/servers/AssertionsServer/AssertionsServer.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Assertions Server""" + +__version__ = '$Revision$'[11:-2] + + +import md5 +import sys +import time +import whrandom + +glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install +sys.path.insert(0, glasnostPythonDir) + +import glasnost + +import glasnost.common.context as context +import glasnost.common.faults as faults + +import glasnost.server.ObjectsServer as objects + + +applicationName = 'AssertionsServer' +applicationRole = 'assertions' +dispatcher = None + +expirationTime = 1 * 60 + + +class AssertionsVirtualServer(objects.VirtualServer): + assertions = None + assertionExpirationTimes = None + + def init(self): + objects.VirtualServer.init(self) + self.assertions = {} + self.assertionExpirationTimes = {} + + +class AssertionsServer(objects.Server): + VirtualServer = AssertionsVirtualServer + + def addAssertion(self, assertion): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + virtualServer.lock.acquire() + while 1: + digest = md5.new(assertion) + randomSalt = str(self.randomGenerator.uniform(0.1, 1))[2:] + digest.update(randomSalt) + artifact = digest.hexdigest() + if not virtualServer.assertions.has_key(artifact): + break + virtualServer.assertions[artifact] = assertion + virtualServer.assertionExpirationTimes[artifact] \ + = time.time() + expirationTime + virtualServer.lock.release() + virtualServer.markCoreAsDirty() + return artifact + + def convertVirtualServersIds(self, sourceDispatcherId, + destinationDispatcherId): + # Erase the assertions when converting ids. + return 0 + + def getAssertion(self, artifact): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + + # Remove expired assertions. + currentTime = time.time() + for artifact2 in virtualServer.assertions.keys(): + if currentTime > virtualServer.assertionExpirationTimes[artifact2]: + del virtualServer.assertions[artifact2] + del virtualServer.assertionExpirationTimes[artifact2] + + if not virtualServer.assertions.has_key(artifact): + raise faults.WrongArtifact(artifact) + assertion = virtualServer.assertions[artifact] + del virtualServer.assertions[artifact] + del virtualServer.assertionExpirationTimes[artifact] + virtualServer.markCoreAsDirty() + return assertion + + def init(self): + self.randomGenerator = whrandom.whrandom() + objects.Server.init(self) + + def registerPublicMethods(self): + objects.Server.registerPublicMethods(self) + self.registerPublicMethod('addAssertion') + self.registerPublicMethod('getAssertion') + + +assertionsServer = AssertionsServer() + + +if __name__ == "__main__": + assertionsServer.launch(applicationName, applicationRole) + diff --git a/servers/AuthenticationLoginPasswordServer/AuthenticationLoginPasswordServer.py b/servers/AuthenticationLoginPasswordServer/AuthenticationLoginPasswordServer.py index dac314ab..7079c859 100755 --- a/servers/AuthenticationLoginPasswordServer/AuthenticationLoginPasswordServer.py +++ b/servers/AuthenticationLoginPasswordServer/AuthenticationLoginPasswordServer.py @@ -456,7 +456,7 @@ class AuthenticationLoginPasswordServer( AuthenticationMethodServer.registerPublicMethods(self) self.registerPublicMethod('emailPassword') - def upgrade_0001_0022(self, virtualServer): + def upgradeVirtualServer_0001_0022(self, virtualServer): if not hasattr(virtualServer, 'authentications'): return if not virtualServer.authentications: diff --git a/servers/AuthenticationServer/AuthenticationServer.py b/servers/AuthenticationServer/AuthenticationServer.py index d7551812..e32ea955 100755 --- a/servers/AuthenticationServer/AuthenticationServer.py +++ b/servers/AuthenticationServer/AuthenticationServer.py @@ -77,7 +77,8 @@ applicationRole = 'authentication' dispatcher = None # Don't forget to also change the value in SessionsServer.py. -expirationTime = 120 +expirationTime = 120 * 60 + class AdminAuthentication(AdminServerMixin, AdminAuthenticationCommon): pass diff --git a/servers/BallotsServer/BallotsServer.py b/servers/BallotsServer/BallotsServer.py index 148742cb..398e5211 100755 --- a/servers/BallotsServer/BallotsServer.py +++ b/servers/BallotsServer/BallotsServer.py @@ -136,9 +136,10 @@ class BallotsServer(Server): def abstainForVote(self, electionId): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - peopleProxy = getProxyForServerRole('people') + identitiesProxy = getProxyForServerRole('identities') try: - previousVoteToken = peopleProxy.getElectionVoteToken(electionId) + previousVoteToken = identitiesProxy.getElectionVoteToken( + electionId) except faults.MissingItem: previousVoteToken = '' if previousVoteToken \ @@ -149,7 +150,7 @@ class BallotsServer(Server): del virtualServer.electionIds[previousVote.electionToken] del virtualServer.voterIds[previousVote.voterToken] del virtualServer.voteIds[previousVote.token] - peopleProxy.abstainForVote(electionId) + identitiesProxy.abstainForVote(electionId) getProxyForServerRole('elections').abstainForVote( electionId, previousVoteToken) votesProxy.deleteObject(voteId) @@ -159,7 +160,7 @@ class BallotsServer(Server): def canGetVoteVoterId(self, voteId): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() vote = getProxyForServerRole('votes').getObject(voteId) if not virtualServer.voterIds.has_key(vote.voterToken): return 0 @@ -188,7 +189,7 @@ class BallotsServer(Server): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) votesExport = [] - peopleProxy = getProxyForServerRole('people') + identitiesProxy = getProxyForServerRole('identities') votesProxy = getProxyForServerRole('votes') for voteToken in voteTokens: if virtualServer.voteIds.has_key(voteToken): @@ -198,7 +199,7 @@ class BallotsServer(Server): if virtualServer.voterIds.has_key(vote.voterToken): voterId = virtualServer.voterIds[vote.voterToken] if voterId in electionVoterIds \ - and peopleProxy.hasObject(voterId): + and identitiesProxy.hasObject(voterId): votesExport.append(vote.exportToXmlRpc()) return votesExport @@ -207,7 +208,7 @@ class BallotsServer(Server): virtualServer = self.getVirtualServer(virtualServerId) electionVoterIds = weightingsById.keys() weightings = {} - peopleProxy = getProxyForServerRole('people') + identitiesProxy = getProxyForServerRole('identities') votesProxy = getProxyForServerRole('votes') for voteToken in voteTokens: if virtualServer.voteIds.has_key(voteToken): @@ -217,7 +218,7 @@ class BallotsServer(Server): if virtualServer.voterIds.has_key(vote.voterToken): voterId = virtualServer.voterIds[vote.voterToken] if voterId in electionVoterIds \ - and peopleProxy.hasObject(voterId): + and identitiesProxy.hasObject(voterId): weightings[vote.voterToken] = weightingsById[ voterId] return weightings @@ -225,7 +226,7 @@ class BallotsServer(Server): def getVote(self, voteId): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() vote = getProxyForServerRole('votes').getObject(voteId) if not virtualServer.voterIds.has_key(vote.voterToken): raise faults.UnknownVoterToken(vote.voterToken) @@ -253,7 +254,7 @@ class BallotsServer(Server): virtualServer = self.getVirtualServer(virtualServerId) if voteToken == 'secret': return 'secret' - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() if not virtualServer.voteIds.has_key(voteToken): raise faults.UnknownVoteToken(voteToken) voteId = virtualServer.voteIds[voteToken] @@ -281,7 +282,7 @@ class BallotsServer(Server): def getVotesFromTokens(self, voteTokens): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() votesExport = {} electionsProxy = getProxyForServerRole('elections') votesProxy = getProxyForServerRole('votes') @@ -317,7 +318,7 @@ class BallotsServer(Server): def getVoteVoterId(self, voteId): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() vote = getProxyForServerRole('votes').getObject(voteId) if not virtualServer.voterIds.has_key(vote.voterToken): raise faults.UnknownVoterToken(vote.voterToken) @@ -346,7 +347,7 @@ class BallotsServer(Server): virtualServer = self.getVirtualServer(virtualServerId) if voteToken == 'secret': return 'secret' - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() if not virtualServer.voteIds.has_key(voteToken): raise faults.UnknownVoteToken(voteToken) voteId = virtualServer.voteIds[voteToken] @@ -414,9 +415,10 @@ class BallotsServer(Server): def vote(self, electionId, voteImport): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - peopleProxy = getProxyForServerRole('people') + identitiesProxy = getProxyForServerRole('identities') try: - previousVoteToken = peopleProxy.getElectionVoteToken(electionId) + previousVoteToken = identitiesProxy.getElectionVoteToken( + electionId) except faults.MissingItem: previousVoteToken = '' @@ -461,12 +463,12 @@ class BallotsServer(Server): voteId = getProxyForServerRole('votes').addObject( vote, serverId = commonTools.extractServerId(electionId)) - getProxyForServerRole('people').vote(electionId, voteToken) + getProxyForServerRole('identities').vote(electionId, voteToken) getProxyForServerRole('elections').vote( electionId, voteToken, previousVoteToken) virtualServer.electionIds[electionToken] = electionId - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() virtualServer.voterIds[voterToken] = userId virtualServer.voteIds[voteToken] = voteId diff --git a/servers/CommentsServer/CommentsServer.py b/servers/CommentsServer/CommentsServer.py index e79aeedf..e24a27ac 100755 --- a/servers/CommentsServer/CommentsServer.py +++ b/servers/CommentsServer/CommentsServer.py @@ -89,7 +89,7 @@ class Comment(ObjectServerMixin, CommentCommon): def setAutomaticalSlots(self, parentSlot = None): ObjectServerMixin.setAutomaticalSlots(self, parentSlot = parentSlot) - self.authorId = getProxyForServerRole('authentication').getUserId() + self.authorId = getProxyForServerRole('identities').getUserId() self.creationTime = time.time() register(Comment) diff --git a/servers/ElectionsServer/ElectionsServer.py b/servers/ElectionsServer/ElectionsServer.py index eb7dabad..c3a10c83 100755 --- a/servers/ElectionsServer/ElectionsServer.py +++ b/servers/ElectionsServer/ElectionsServer.py @@ -374,9 +374,9 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): # User must be an admin or (a writer and a writer of this object)) if not self.isAdmin() and not ( self.useAdminWritersSet - and getProxyForServerRole('authentication').setContainsUser( + and getProxyForServerRole('identities').setContainsUser( self.getAdminCore().writersSet) and ( - getProxyForServerRole('authentication').setContainsUser( + getProxyForServerRole('identities').setContainsUser( object.writersSet))) \ or object.state != 'draft': if not object.canBeCreatedByClient(): @@ -397,7 +397,7 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): return 0 object = virtualServer.loadObjectCore(objectId) isAdmin = self.isAdmin() - if not isAdmin and not getProxyForServerRole('authentication' + if not isAdmin and not getProxyForServerRole('identities' ).setContainsUser(object.writersSet): return 0 if not isAdmin and object.state != 'draft': @@ -413,7 +413,7 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): if not object.canBeModified(): return 0 isAdmin = self.isAdmin() - if not isAdmin and not getProxyForServerRole('authentication' + if not isAdmin and not getProxyForServerRole('identities' ).setContainsUser(object.writersSet): return 0 if not isAdmin and object.state != 'draft': @@ -455,12 +455,14 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): possibleAuthorIds = 'everybody' try: possibleReaderIds = getSetContainedIds( - possibleReadersSet, ['people'], raiseWhenUncountable = 1) + possibleReadersSet, ['identities'], + raiseWhenUncountable = 1) except faults.UncountableGroup: possibleReaderIds = 'everybody' try: possibleWriterIds = getSetContainedIds( - possibleWritersSet, ['people'], raiseWhenUncountable = 1) + possibleWritersSet, ['identities'], + raiseWhenUncountable = 1) except faults.UncountableGroup: possibleWriterIds = 'everybody' objectIds = virtualServer.objects.keys() @@ -475,7 +477,7 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): if possibleStates and not object.state in possibleStates: continue if not isAdmin and not getProxyForServerRole( - 'authentication').setContainsUser(object.readersSet): + 'identities').setContainsUser(object.readersSet): continue if not self.getLastObjectIds_filter( possibleAuthorIds, 1, object.authorsSet): @@ -504,7 +506,7 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): for objectId in objectIds: object = virtualServer.loadObjectCore(objectId) if not self.isAdmin() \ - and not getProxyForServerRole('authentication' + and not getProxyForServerRole('identities' ).setContainsUser(object.readersSet): continue if possibleStates and not object.state in possibleStates: @@ -540,7 +542,7 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): if not self.canModifyObject(object.id) or not ( self.isAdmin() or not objectChanges.hasSlotName('writersSet') - or getProxyForServerRole('authentication' + or getProxyForServerRole('identities' ).setContainsUser( objectChanges.getSlot('writersSet').getValue())): if not object.canBeModifiedByClient(): @@ -619,7 +621,7 @@ class ElectionsServer(ElectionsCommonMixin, ObjectsServer): fromAddress = virtualServer.adminEmailAddress abstentionnistsCount = 0 for voterId in object.getVoterIds(virtualServerId): - voter = getProxyForServerRole('people').getObject(voterId) + voter = getProxyForServerRole('identities').getObject(voterId) if voter.voteTokens is not None \ and voter.voteTokens.has_key(objectId): continue diff --git a/servers/GroupsServer/GroupsServer.py b/servers/GroupsServer/GroupsServer.py index d4ae2fa7..d3aabf35 100755 --- a/servers/GroupsServer/GroupsServer.py +++ b/servers/GroupsServer/GroupsServer.py @@ -122,8 +122,8 @@ class GroupsServer(commonGroups.GroupsCommonMixin, objects.ObjectsServer): if not self.isAdmin(): clientToken = context.getVar('clientToken') clientId = getApplicationId(clientToken) - clientNameAndPort, clientRole, mu = commonTools.splitId(clientId) - if clientRole != 'people': + clientRole = commonTools.extractRole(clientId) + if clientRole != 'identities': raise faults.UserAccessDenied() object = virtualServer.loadObjectCore(objectId) if not isinstance(object, GroupUnion): @@ -155,8 +155,8 @@ class GroupsServer(commonGroups.GroupsCommonMixin, objects.ObjectsServer): if not self.isAdmin(): clientToken = context.getVar('clientToken') clientId = getApplicationId(clientToken) - clientNameAndPort, clientRole, mu = commonTools.splitId(clientId) - if clientRole != 'people': + clientRole = commonTools.extractRole(clientId) + if clientRole != 'identities': raise faults.UserAccessDenied() object = virtualServer.loadObjectCore(objectId) if not isinstance(object, GroupUnion): diff --git a/servers/IdentitiesServer/IdentitiesServer.py b/servers/IdentitiesServer/IdentitiesServer.py new file mode 100755 index 00000000..76efd130 --- /dev/null +++ b/servers/IdentitiesServer/IdentitiesServer.py @@ -0,0 +1,411 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Server""" + +__version__ = '$Revision$'[11:-2] + + +import md5 +import sys +import time +import whrandom + +glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install +sys.path.insert(0, glasnostPythonDir) + +import glasnost + +import glasnost.common.context as context +import glasnost.common.faults as faults +import glasnost.common.IdentitiesCommon as commonIdentities +import glasnost.common.tools_new as commonTools + +import glasnost.server.ObjectsServer as objects +import glasnost.server.things as things + +from glasnost.proxy.CacheProxy import invalidateValue +from glasnost.proxy.DispatcherProxy import getApplicationId +from glasnost.proxy.GroupsProxy import setContains +from glasnost.proxy.tools import getProxy + + +applicationName = 'IdentitiesServer' +applicationRole = 'identities' +dispatcher = None + +# Don't forget to also change the value in SessionsServer.py. +expirationTime = 120 * 60 + + +# FIXME: those classes are necessary to upgrade data from +# PeopleServer.pickle & PreferencesServer.pickle; they should be removed as +# soon as it is no longer necessary to access these files. +class AdminPeople: pass +class PeopleVirtualServer: pass +class Person: pass +class PreferencesVirtualServer: pass + + +class AdminIdentities(objects.AdminServerMixin, + commonIdentities.AdminIdentities): + pass +objects.register(AdminIdentities) + + +class Identification(things.ThingMixin, commonIdentities.Identification): + pass +things.register(Identification) + + +class Identity(objects.ObjectServerMixin, commonIdentities.Identity): + def abstainForVote(self, electionId): + if self.voteTokens is not None and self.voteTokens.has_key(electionId): + del self.voteTokens[electionId] + if len(self.voteTokens) == 0: + del self.voteTokens + + def exportToXmlRpc(self, requiredSlotNames = None, parentSlot = None): + identityExport = commonIdentities.Identity.exportToXmlRpc( + self, requiredSlotNames = requiredSlotNames, + parentSlot = parentSlot) + + userId = self.getServer().getUserId() + if self.id != userId and identityExport.has_key('voteTokens'): + if not userId: + del identityExport['voteTokens'] + else: + voteTokens = identityExport['voteTokens'].copy() + for electionId, voteToken in voteTokens.items(): + electionsProxy = getProxy(electionId) + if not electionsProxy.hasObject(electionId): + del voteTokens[electionId] + else: + electionBallotKind = electionsProxy.getBallotKind( + electionId) + if electionBallotKind == 'secret' \ + or electionBallotKind == 'voterChoice' \ + and getProxyForServerRole( + 'ballots').isVoteSecret(voteToken): + voteTokens[electionId] = 'secret' + if len(voteTokens) == 0: + del identityExport['voteTokens'] + else: + identityExport['voteTokens'] = voteTokens + return identityExport + + def vote(self, electionId, voteToken): + if self.voteTokens is None: + self.voteTokens = {} + self.voteTokens[electionId] = voteToken +objects.register(Identity) + + +class IdentitiesVirtualServer(objects.ObjectsVirtualServer): + idsByToken = None + tokenExpirationTimes = None + + def convertIds(self, sourceDispatcherId, destinationDispatcherId): + objects.ObjectsVirtualServer.convertIds( + self, sourceDispatcherId, destinationDispatcherId) + + for token, expiration in self.tokenExpirationTimes.items(): + newToken = token.replace(sourceDispatcherId, + destinationDispatcherId) + if newToken != token: + del self.tokenExpirationTimes[token] + self.tokenExpirationTimes[newToken] = expiration + + for token, id in self.idsByToken.items(): + newToken = token.replace(sourceDispatcherId, + destinationDispatcherId) + if newToken != token: + del self.idsByToken[token] + token = newToken + self.idsByToken[token] = id + newId = id.replace(sourceDispatcherId, destinationDispatcherId) + if newId != id: + self.idsByToken[token] = newId + + def getObjectToken(self, object): + self.removeExpiredTokens() + + self.lock.acquire() + for token, id in self.idsByToken.items(): + if id == object.id: + objectToken = token + self.tokenExpirationTimes[token] = time.time() + expirationTime + break + else: + server = context.getVar('server') + while 1: + randomSalt = str(server.randomGenerator.uniform(0.1, 1))[2:] + digest = md5.new(randomSalt) + localToken = digest.hexdigest() + objectToken = '%s/%s' % (self.virtualServerId, localToken) + if not self.idsByToken.has_key(objectToken): + break + self.idsByToken[objectToken] = object.id + self.tokenExpirationTimes[objectToken] = time.time() \ + + expirationTime + self.lock.release() + self.markCoreAsDirty() + return objectToken + + def init(self): + objects.ObjectsVirtualServer.init(self) + self.idsByToken = {} + self.tokenExpirationTimes = {} + + def removeExpiredTokens(self): + currentTime = time.time() + removed = 0 + self.lock.acquire() + for token in self.tokenExpirationTimes.keys(): + if currentTime > self.tokenExpirationTimes[token]: + removed = 1 + del self.idsByToken[token] + del self.tokenExpirationTimes[token] + self.lock.release() + if removed: + self.markCoreAsDirty() + + +class IdentitiesServer(commonIdentities.IdentitiesCommonMixin, + objects.ObjectsServer): + VirtualServer = IdentitiesVirtualServer + + def abstainForVote(self, electionId): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + voterId = self.getUserId() + object = virtualServer.loadObjectCore(voterId) + object.abstainForVote(electionId) + virtualServer.markObjectAsDirty(object) + invalidateValue(object.id) + + def checkIdentityLocalNameIdentifier( + self, peerHostName, localNameIdentifier): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + for object in virtualServer.objects.values(): + if object.identityIdentifications is not None: + for identityIdentification in object.identityIdentifications: + if identityIdentification.peerHostName == peerHostName \ + and identityIdentification.localNameIdentifier \ + == localNameIdentifier: + return virtualServer.getObjectToken(object) + raise faults.WrongNameIdentifier(localNameIdentifier) + + def checkIdentityPeerNameIdentifier( + self, peerHostName, peerNameIdentifier): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + for object in virtualServer.objects.values(): + if object.identityIdentifications is not None: + for identityIdentification in object.identityIdentifications: + if identityIdentification.peerHostName == peerHostName \ + and identityIdentification.peerNameIdentifier \ + == peerNameIdentifier: + return virtualServer.getObjectToken(object) + raise faults.WrongNameIdentifier(peerNameIdentifier) + + def deleteUserToken(self): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + userToken = context.getVar('userToken') + if virtualServer.idsByToken.has_key(userToken): + del virtualServer.idsByToken[userToken] + del virtualServer.tokenExpirationTimes[userToken] + + def fillEmptyVirtualServer(self, virtualServer): + objects.ObjectsServer.fillEmptyVirtualServer(self, virtualServer) + + # Upgrade to version 0001_0028. + import cPickle + import os + peoplePickleFilePath = os.path.join(virtualServer.dataDirectoryPath, + 'PeopleServer.pickle') + if os.access(peoplePickleFilePath, os.F_OK): + print 'Importing PeopleServer data for %s.' \ + % virtualServer.virtualServerId + peopleRcFile = open(peoplePickleFilePath, 'rb') + peopleVersion = self.readFileVersion(peopleRcFile) + peopleVirtualServer = cPickle.load(peopleRcFile) + peopleRcFile.close() + + preferencesPickleFilePath = os.path.join( + virtualServer.dataDirectoryPath, + 'PreferencesServer.pickle') + hasPreferences = 0 + if os.access(peoplePickleFilePath, os.F_OK): + hasPreferences = 1 + print 'Importing PreferencesServer data for %s.' \ + % virtualServer.virtualServerId + preferencesRcFile = open(preferencesPickleFilePath, 'rb') + preferencesVersion = self.readFileVersion(preferencesRcFile) + preferencesVirtualServer = cPickle.load(preferencesRcFile) + preferencesRcFile.close() + + personIds = peopleVirtualServer.objects.keys() + personIds.sort() # PasswordAccountsServer uses the same order. + for personId in personIds: + person = peopleVirtualServer.objects[personId] + identity = Identity() + identity.personId = personId + if hasattr(person, 'voteTokens'): + identity.voteTokens = person.voteTokens + if hasPreferences \ + and preferencesVirtualServer.objects.has_key(personId): + preference = preferencesVirtualServer.objects[personId] + if preference.has_key('objectsMemory'): + identity.objectsMemory = preference['objectsMemory'] + if preference.has_key('spellcheckEntries'): + identity.objectsMemory = preference[ + 'spellcheckEntries'] + identity.setAutomaticalSlots() + virtualServer.objects[identity.id] = identity + identity.saveNonCore() + identity.releaseNonCore() + virtualServer.markObjectAsDirty(identity) + virtualServer.markCoreAsDirty() + + def getElectionVoteToken(self, electionId): + clientToken = context.getVar('clientToken') + clientId = getApplicationId(clientToken) + clientRole = commonTools.extractRole(clientId) + if clientRole != 'ballots': + raise faults.UserAccessDenied() + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + voterId = self.getUserId() + object = virtualServer.loadObjectCore(voterId) + if object.voteTokens is None \ + or not object.voteTokens.has_key(electionId): + raise faults.MissingItem(electionId) + return object.voteTokens[electionId] + + def getUserId(self): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + userToken = context.getVar('userToken') + + virtualServer.removeExpiredTokens() + if not virtualServer.idsByToken.has_key(userToken): + return '' + virtualServer.tokenExpirationTimes[userToken] = time.time() \ + + expirationTime + virtualServer.markCoreAsDirty() + return virtualServer.idsByToken[userToken] + + def getUserToken(self, id): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + clientToken = context.getVar('clientToken') + clientId = getApplicationId(clientToken) + clientRole = commonTools.extractRole(clientId) + if not clientRole in ['passwordaccounts', 'x509accounts']: + raise faults.ApplicationAccessDenied(clientId) + object = virtualServer.loadObjectCore(id) + return virtualServer.getObjectToken(object) + + def init(self): + self.randomGenerator = whrandom.whrandom() + objects.ObjectsServer.init(self) + + def registerPublicMethods(self): + objects.ObjectsServer.registerPublicMethods(self) + self.registerPublicMethod('abstainForVote') + self.registerPublicMethod('checkIdentityLocalNameIdentifier') + self.registerPublicMethod('checkIdentityPeerNameIdentifier') + self.registerPublicMethod('deleteUserToken') + self.registerPublicMethod('getElectionVoteToken') + self.registerPublicMethod('getUserId') + self.registerPublicMethod('getUserToken') + self.registerPublicMethod('rememberId') + self.registerPublicMethod('setContainsUser') + self.registerPublicMethod('vote') + + def rememberId(self, id): + virtualServerId = context.getVar('applicationId') + if virtualServerId == 'glasnost://system/identities': + return + virtualServer = self.getVirtualServer(virtualServerId) + userId = self.getUserId() + serverRole = commonTools.extractRole(id) + object = virtualServer.loadObjectCore(userId) + if object.objectsMemory is None: + object.objectsMemory = {} + objectsMemory = object.objectsMemory + if objectsMemory.has_key(serverRole): + serverMemory = objectsMemory[serverRole] + else: + serverMemory = None + if serverMemory is None: + serverMemory = objectsMemory[serverRole] = [] + if id in serverMemory: + serverMemory.remove(id) + serverMemory.insert(0, id) + if len(serverMemory) > 10: + del serverMemory[10:] + virtualServer.markObjectAsDirty(object) + invalidateValue(object.id) + + def setContainsUser(self, set): + return setContains(set, self.getUserId()) + + def vote(self, electionId, voteToken): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + voterId = self.getUserId() + object = virtualServer.loadObjectCore(voterId) + object.vote(electionId, voteToken) + virtualServer.markObjectAsDirty(object) + invalidateValue(object.id) + + +identitiesServer = IdentitiesServer() + + +if __name__ == "__main__": + identitiesServer.launch(applicationName, applicationRole) diff --git a/servers/PasswordAccountsServer/PasswordAccountsServer.py b/servers/PasswordAccountsServer/PasswordAccountsServer.py new file mode 100755 index 00000000..17291c6e --- /dev/null +++ b/servers/PasswordAccountsServer/PasswordAccountsServer.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Password Accounts Server""" + +__version__ = '$Revision$'[11:-2] + + +import copy +import sys + +glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install +sys.path.insert(0, glasnostPythonDir) + +import glasnost + +import glasnost.common.context as context +import glasnost.common.faults as faults +import glasnost.common.PasswordAccountsCommon as commonPasswordAccounts +from glasnost.common.tools import iso8859_15 +import glasnost.common.tools_new as commonTools + +import glasnost.server.ObjectsServer as objects + +from glasnost.proxy.tools import getProxy + + +applicationName = 'PasswordAccountsServer' +applicationRole = 'passwordaccounts' +dispatcher = None + + +# FIXME: those classes are necessary to upgrade data from +# AuthenticationLoginPasswordServer.pickle; they should be removed as +# soon as it is no longer necessary to access this file. +class AccountLoginPassword: pass +class AdminAuthenticationLoginPassword: pass +class AuthenticationLoginPasswordVirtualServer: pass + + +class AdminPasswordAccounts(objects.AdminServerMixin, + commonPasswordAccounts.AdminPasswordAccounts): + def checkModifyIsPossible(self, changes, givenSlotNames = None): + # This change is irreversible. + if self.storePasswordsInClearText == 0 \ + and changes.storePasswordsInClearText == 1: + raise faults.UnableToChangePasswordStorage() + objects.AdminServerMixin.checkModifyIsPossible( + self, changes, givenSlotNames = givenSlotNames) +objects.register(AdminPasswordAccounts) + + +class PasswordAccount(objects.ObjectServerMixin, + commonPasswordAccounts.PasswordAccount): + def checkModifyIsPossible(self, changes, givenSlotNames = None): + objects.ObjectServerMixin.checkModifyIsPossible( + self, changes, givenSlotNames = givenSlotNames) + virtualServerId = context.getVar('applicationId') + virtualServer = self.getServer().getVirtualServer(virtualServerId) + if (not givenSlotNames or 'login' in givenSlotNames) \ + and changes.login != self.login and changes.login is not None: + if virtualServer.objectsByLogin.has_key(changes.login) \ + and changes.id != virtualServer.objectsByLogin[ + changes.login].id: + raise faults.DuplicateLogin(changes.login) + + def clear(self): + objectsByLogin = self.getServer().virtualServer.objectsByLogin + if objectsByLogin.has_key(self.login): + del objectsByLogin[self.login] + + def modify(self, changes, givenSlotNames = None): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getServer().getVirtualServer(virtualServerId) + login = self.login + if not virtualServer.admin.userCanChoosePassword: + self.password_kind = copy.copy(self.password_kind) + self.password_kind.hasToModify = 0 + objects.ObjectServerMixin.modify( + self, changes, givenSlotNames = givenSlotNames) + if not virtualServer.admin.userCanChoosePassword: + del self.password_kind + if self.login != login: + if login is not None: + del virtualServer.objectsByLogin[login] + if self.login is not None: + virtualServer.objectsByLogin[self.login] = self +objects.register(PasswordAccount) + + +class PasswordAccountsVirtualServer(objects.ObjectsVirtualServer): + objectsByLogin = None + + def init(self): + objects.ObjectsVirtualServer.init(self) + self.objectsByLogin = {} + + +class PasswordAccountsServer( + commonPasswordAccounts.PasswordAccountsCommonMixin, + objects.ObjectsServer): + VirtualServer = PasswordAccountsVirtualServer + + def addObjectXmlRpc(self, objectImport): + objectId = objects.ObjectsServer.addObjectXmlRpc(self, objectImport) + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + object = virtualServer.loadObjectCore(objectId) + if virtualServer.objectsByLogin.has_key(object.login): + # Login already used. + del virtualServer.objects[objectId] + virtualServer.markObjectAsDeleted(objectId) + virtualServer.markCoreAsDirty() + raise faults.DuplicateLogin(object.login) + + virtualServer.objectsByLogin[object.login] = object + virtualServer.markCoreAsDirty() + return objectId + + def checkObjectAuthenticationXmlRpc(self, loginImport, passwordImport): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + login = iso8859_15(loginImport) + password = iso8859_15(passwordImport) + if not virtualServer.objectsByLogin.has_key(login): + raise faults.WrongLogin(login) + object = virtualServer.objectsByLogin[login] + if object.password and password != object.password: + raise faults.WrongPassword(password) + identitiesProxy = getProxy(object.identityId) + return identitiesProxy.getUserToken(object.identityId) + + def fillEmptyVirtualServer(self, virtualServer): + objects.ObjectsServer.fillEmptyVirtualServer(self, virtualServer) + + # Upgrade to version 0001_0028. + import cPickle + import os + authenticationPickleFilePath = os.path.join( + virtualServer.dataDirectoryPath, + 'AuthenticationLoginPasswordServer.pickle') + if os.access(authenticationPickleFilePath, os.F_OK): + print 'Importing AuthenticationLoginPasswordServer data for %s.' \ + % virtualServer.virtualServerId + authenticationRcFile = open(authenticationPickleFilePath, 'rb') + authenticationVersion = self.readFileVersion(authenticationRcFile) + authenticationVirtualServer = cPickle.load(authenticationRcFile) + authenticationRcFile.close() + + admin = virtualServer.admin + authenticationAdmin = authenticationVirtualServer.admin + if hasattr(authenticationAdmin, 'stockPasswordsInClearText'): + admin.storePasswordsInClearText \ + = authenticationAdmin.stockPasswordsInClearText + if hasattr(authenticationAdmin, 'userCanChoosePassword'): + admin.userCanChoosePassword \ + = authenticationAdmin.userCanChoosePassword + virtualServer.markAdminAsDirty(virtualServer.admin) + + personIds = authenticationVirtualServer.authentications.keys() + personIds.sort() # IdentitiesServer uses the same order. + for personId in personIds: + authentication = authenticationVirtualServer.authentications[ + personId] + passwordAccount = PasswordAccount() + passwordAccount.login = authentication.login + passwordAccount.password = authentication.password + passwordAccount.setAutomaticalSlots() + passwordAccount.identityId = '%s/%s' % ( + commonTools.makeApplicationId(passwordAccount.id, + 'identities'), + commonTools.extractLocalId(passwordAccount.id)) + virtualServer.objects[passwordAccount.id] = passwordAccount + virtualServer.objectsByLogin[ + passwordAccount.login] = passwordAccount + passwordAccount.saveNonCore() + passwordAccount.releaseNonCore() + virtualServer.markObjectAsDirty(passwordAccount) + virtualServer.markCoreAsDirty() + + + def registerPublicMethods(self): + objects.ObjectsServer.registerPublicMethods(self) + self.registerPublicMethod('checkObjectAuthentication', + self.checkObjectAuthenticationXmlRpc) + + +passwordAccountsServer = PasswordAccountsServer() + + +if __name__ == "__main__": + passwordAccountsServer.launch(applicationName, applicationRole) diff --git a/servers/PeopleServer/PeopleServer.py b/servers/PeopleServer/PeopleServer.py index 7c7c08df..eca1d02c 100755 --- a/servers/PeopleServer/PeopleServer.py +++ b/servers/PeopleServer/PeopleServer.py @@ -85,22 +85,6 @@ register(AdminPeople) class Person(ObjectServerMixin, PersonCommon): - def abstainForVote(self, electionId): - """Drop the person election token. - - Keyword arguments: - ================== - - *electionId*: - The Id of the election to abstain. - - """ - - if self.voteTokens is not None and self.voteTokens.has_key(electionId): - del self.voteTokens[electionId] - if len(self.voteTokens) == 0: - del self.voteTokens - def checkAddIsPossible(self): ObjectServerMixin.checkAddIsPossible(self) virtualServerId = context.getVar('applicationId') @@ -146,49 +130,6 @@ class Person(ObjectServerMixin, PersonCommon): if person.fingerprint == changes.fingerprint \ and person.id != changes.id: raise faults.DuplicateFingerprint(changes.fingerprint) - - def exportToXmlRpc(self, requiredSlotNames = None, parentSlot = None): - personExport = PersonCommon.exportToXmlRpc( - self, requiredSlotNames = requiredSlotNames, - parentSlot = parentSlot) - - userId = getProxyForServerRole('authentication').getUserId() - if self.id != userId and not self.getServer().isAdmin(): - #if personExport.has_key('email') and not userId: - # del personExport['email'] - if personExport.has_key('voteTokens'): - if not userId: - del personExport['voteTokens'] - else: - voteTokens = personExport['voteTokens'].copy() - for electionId, voteToken in voteTokens.items(): - if not getProxyForServerRole('elections').hasObject( - electionId): - del voteTokens[electionId] - else: - electionBallotKind = getProxyForServerRole( - 'elections').getBallotKind(electionId) - if electionBallotKind == 'secret' \ - or electionBallotKind == 'voterChoice' \ - and getProxyForServerRole( - 'ballots').isVoteSecret(voteToken): - voteTokens[electionId] = 'secret' - if len(voteTokens) == 0: - del personExport['voteTokens'] - else: - personExport['voteTokens'] = voteTokens - return personExport - - def modify(self, changes, givenSlotNames = None): - virtualServerId = context.getVar('applicationId') - virtualServer = self.getServer().getVirtualServer(virtualServerId) - ObjectServerMixin.modify( - self, changes, givenSlotNames = givenSlotNames) - - def vote(self, electionId, voteToken): - if self.voteTokens is None: - self.voteTokens = {} - self.voteTokens[electionId] = voteToken register(Person) @@ -234,36 +175,6 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): VirtualServer = PeopleVirtualServer - def abstainForVote(self, electionId): - """Abstain voting on an election. - - Keyword argument: - ================= - - *electionId*: - The Id of the election to abstain. - - Return 0 everytime. - - Exceptions: - =========== - - *faults.MissingItem*: - The specified election does not exist on the specified server. - - *AttributeError*: - No authentication proxy found. - - """ - - virtualServerId = context.getVar('applicationId') - virtualServer = self.getVirtualServer(virtualServerId) - voterId = getProxyForServerRole('authentication').getUserId() - object = virtualServer.loadObjectCore(voterId) - object.abstainForVote(electionId) - virtualServer.markObjectAsDirty(object) - invalidateValue(object.id) - def addObjectXmlRpc(self, objectImport): """Create a new person on the server. @@ -334,7 +245,7 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): return 0 object = virtualServer.loadObjectCore(objectId) return self.isAdmin() \ - or getProxyForServerRole('authentication').setContainsUser( + or getProxyForServerRole('identities').setContainsUser( [object.id]) def canGetObject(self, objectId): @@ -344,7 +255,7 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): virtualServer = self.getVirtualServer(virtualServerId) if not virtualServer.canLoadObjectCore(objectId): return 0 - return getProxyForServerRole('authentication').setContainsUser( + return getProxyForServerRole('identities').setContainsUser( [objectId]) @@ -355,7 +266,7 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): return 0 object = virtualServer.loadObjectCore(objectId) return self.isAdmin() \ - or getProxyForServerRole('authentication').setContainsUser( + or getProxyForServerRole('identities').setContainsUser( [object.id]) def deleteObject(self, objectId): @@ -409,21 +320,6 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): if object.containsText(text): foundIds.append(objectId) return foundIds - - def getElectionVoteToken(self, electionId): - clientToken = context.getVar('clientToken') - clientId = getApplicationId(clientToken) - clientRole = commonTools.extractRole(clientId) - if clientRole != 'ballots': - raise faults.UserAccessDenied() - voterId = getProxyForServerRole('authentication').getUserId() - virtualServerId = context.getVar('applicationId') - virtualServer = self.getVirtualServer(virtualServerId) - object = virtualServer.loadObjectCore(voterId) - if object.voteTokens is None \ - or not object.voteTokens.has_key(electionId): - raise faults.MissingItem(electionId) - return object.voteTokens[electionId] def getObjectStringFromDigestXmlRpc(self, objectId, path, digest): """Retrieve a string in the specified object from its MD5 digest. @@ -474,7 +370,7 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): result = getStringFromDigest(object.getLabel(), digest) else: if not self.isAdmin() and not getProxyForServerRole( - 'authentication').setContainsUser([objectId]): + 'identities').setContainsUser([objectId]): raise faults.UserAccessDenied() result = getStringFromDigest( eval(path, {'self': object}), digest) @@ -530,7 +426,7 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): if not object.canBeModified(): raise faults.ReadOnlyObject() if not self.isAdmin() and not getProxyForServerRole( - 'authentication').setContainsUser([object.id]): + 'identities').setContainsUser([object.id]): if not object.canBeModifiedByClient(): raise faults.UserAccessDenied() object.checkModifyIsPossible(objectChanges) @@ -554,10 +450,7 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): """Register the people server XML RPs.""" ObjectsServer.registerPublicMethods(self) - self.registerPublicMethod('abstainForVote') self.registerPublicMethod('findObjectIds', self.findObjectIdsXmlRpc) - self.registerPublicMethod('getElectionVoteToken') - self.registerPublicMethod('vote') def repairVirtualServer(self, virtualServer, version): """Handle a descendant compatibily with older server datas. @@ -622,8 +515,8 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): if changed: virtualServer.markAllAsDirtyFIXME() - def upgrade_0001_0019(self, virtualServer): - ObjectsServer.upgrade_0001_0019(self, virtualServer) + def upgradeVirtualServer_0001_0019(self, virtualServer): + ObjectsServer.upgradeVirtualServer_0001_0019(self, virtualServer) # Fill empty writersSet slots in admin. admin = virtualServer.admin @@ -631,38 +524,6 @@ class PeopleServer(PeopleCommonMixin, ObjectsServer): admin.writersSet = [system.generalPublicId] virtualServer.markAdminAsDirty(admin) - def vote(self, electionId, voteToken): - """Vote to an election. - - Keyword arguments: - ================== - - *electionId*: - The ID of the election to vote. - - *voteToken*: - The vote token associated to the vote. - - Exceptions: - =========== - - *faults.MissingItem*: - The specified election was not found. - - *KeyError*: - The virtual server ID does not correspond to a instanciated virtual - server. - - """ - - virtualServerId = context.getVar('applicationId') - virtualServer = self.getVirtualServer(virtualServerId) - voterId = getProxyForServerRole('authentication').getUserId() - object = virtualServer.loadObjectCore(voterId) - object.vote(electionId, voteToken) - virtualServer.markObjectAsDirty(object) - invalidateValue(object.id) - peopleServer = PeopleServer() diff --git a/servers/PreferencesServer/PreferencesServer.py b/servers/PreferencesServer/PreferencesServer.py index 09b868d3..bfb75e33 100755 --- a/servers/PreferencesServer/PreferencesServer.py +++ b/servers/PreferencesServer/PreferencesServer.py @@ -104,7 +104,7 @@ class PreferencesServer(Server): def deleteObject(self): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() virtualServer.lock.acquire() if virtualServer.objects.has_key(userId): del virtualServer.objects[userId] @@ -113,7 +113,7 @@ class PreferencesServer(Server): #invalidateValue(no id!) def getObject(self): - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() return self.getObjectByUserId(userId) def getObjectByUserId(self, userId): @@ -136,47 +136,9 @@ class PreferencesServer(Server): self.registerPublicMethod('deleteObject') self.registerPublicMethod('getObject') self.registerPublicMethod('getObjectByUserId') - self.registerPublicMethod('rememberId') self.registerPublicMethod('setObject') self.registerPublicMethod('setObjectForUserId') - def rememberId(self, id): - virtualServerId = context.getVar('applicationId') - if virtualServerId == 'glasnost://system/preferences': - return - virtualServer = self.getVirtualServer(virtualServerId) - userId = getProxyForServerRole('authentication').getUserId() - if virtualServer.objects.has_key(userId): - preference = virtualServer.objects[userId] - else: - preference = { - '__thingCategory__': 'object', - '__thingName__': 'preferences.Preference', - 'version': 0, - } - serverHostNameAndPort, serverRole, localId = \ - commonTools.splitId(id) - if preference.has_key('objectsMemory'): - objectsMemory = preference['objectsMemory'] - else: - objectsMemory = None - if objectsMemory is None: - objectsMemory = preference['objectsMemory'] = {} - if objectsMemory.has_key(serverRole): - serverMemory = objectsMemory[serverRole] - else: - serverMemory = None - if serverMemory is None: - serverMemory = objectsMemory[serverRole] = [] - if id in serverMemory: - serverMemory.remove(id) - serverMemory.insert(0, id) - if len(serverMemory) > 10: - serverMemory[10:] = [] - virtualServer.objects[userId] = preference - virtualServer.markCoreAsDirty() - #invalidateValue(no id!) - def repairVirtualServer(self, virtualServer, version): changed = 0 if version < 2006: @@ -229,7 +191,7 @@ class PreferencesServer(Server): def setObject(self, objectChanges): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() version = 0 object = None if virtualServer.objects.has_key(userId): @@ -253,8 +215,8 @@ class PreferencesServer(Server): def setObjectForUserId(self, userId, objectChanges): virtualServerId = context.getVar('applicationId') - peopleProxy = getProxyForServerRole('people') - userTokenId = getProxyForServerRole('authentication').getUserId() + identitiesProxy = getProxyForServerRole('identities') + userTokenId = identitiesProxy.getUserId() virtualServer = self.getVirtualServer(virtualServerId) version = 0 object = None @@ -263,7 +225,7 @@ class PreferencesServer(Server): if object.has_key('version'): version = object['version'] - isAdmin = peopleProxy.isAdmin(serverId = virtualServerId) + isAdmin = identitiesProxy.isAdmin(serverId = virtualServerId) if not (isAdmin or userTokenId == userId): raise faults.UserAccessDenied() diff --git a/servers/ProvidersServer/ProvidersServer.py b/servers/ProvidersServer/ProvidersServer.py new file mode 100755 index 00000000..10f46808 --- /dev/null +++ b/servers/ProvidersServer/ProvidersServer.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Providers Server""" + +__version__ = '$Revision$'[11:-2] + + +import sys + +glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install +sys.path.insert(0, glasnostPythonDir) + +import glasnost + +import glasnost.common.context as context +import glasnost.common.faults as faults +import glasnost.common.ProvidersCommon as commonProviders + +import glasnost.server.ObjectsServer as objects + + +applicationName = 'ProvidersServer' +applicationRole = 'providers' +dispatcher = None + + +class AdminProviders(objects.AdminServerMixin, commonProviders.AdminProviders): + pass +objects.register(AdminProviders) + + +class IdentityProvider(objects.ObjectServerMixin, + commonProviders.IdentityProvider): + pass +objects.register(IdentityProvider) + + +class ServiceProvider(objects.ObjectServerMixin, + commonProviders.ServiceProvider): + pass +objects.register(ServiceProvider) + + +class ProvidersServer(commonProviders.ProvidersCommonMixin, + objects.ObjectsServer): + def getRemoteIdentityProviderId(self): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + admin = virtualServer.admin + if admin.remoteIdentityProviderId is None: + raise faults.MissingItem('Remote Identity Provider') + return admin.remoteIdentityProviderId + + def getServiceProviderId(self, providerId): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + for objectId, objectCore in virtualServer.objects.items(): + if isinstance(objectCore, ServiceProvider) \ + and objectCore.providerId == providerId \ + and (self.canGetObject(objectId) + or objectCore.canBeGottenByClient()): + return objectId + raise faults.MissingItem('Service Provider "%s"' % providerId) + + def registerPublicMethods(self): + objects.ObjectsServer.registerPublicMethods(self) + self.registerPublicMethod('getRemoteIdentityProviderId') + self.registerPublicMethod('getServiceProviderId') + + +providersServer = ProvidersServer() + + +if __name__ == "__main__": + providersServer.launch(applicationName, applicationRole) diff --git a/servers/SessionsServer/SessionsServer.py b/servers/SessionsServer/SessionsServer.py index 152e8fc5..78cdd229 100755 --- a/servers/SessionsServer/SessionsServer.py +++ b/servers/SessionsServer/SessionsServer.py @@ -66,8 +66,8 @@ applicationName = 'SessionsServer' applicationRole = 'sessions' dispatcher = None -# Don't forget to also change the value in AuthenticationServer.py. -expirationTime = 120 +# Don't forget to also change the value in IdentitiesServer.py. +expirationTime = 120 * 60 class SessionsVirtualServer(VirtualServer): @@ -150,8 +150,8 @@ class SessionsServer(Server): def getHistory(self): virtualServerId = context.getVar('applicationId') virtualServer = self.getVirtualServer(virtualServerId) - peopleProxy = getProxyForServerRole('people') - if not peopleProxy.isAdmin( + identitiesProxy = getProxyForServerRole('identities') + if not identitiesProxy.isAdmin( serverId = commonTools.extractServerId(virtualServerId)): raise faults.UserAccessDenied() diff --git a/servers/TranslationsServer/TranslationsServer.py b/servers/TranslationsServer/TranslationsServer.py index cf5229cc..19519d98 100755 --- a/servers/TranslationsServer/TranslationsServer.py +++ b/servers/TranslationsServer/TranslationsServer.py @@ -315,7 +315,7 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin, else: translatorsSet = None result = self.isAdmin() \ - or getProxyForServerRole('authentication').setContainsUser( + or getProxyForServerRole('identities').setContainsUser( translatorsSet) return result @@ -325,9 +325,9 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin, if not virtualServer.admin.translatorsSets.has_key(localizationKey): return 0 translatorsSet = virtualServer.admin.translatorsSets[localizationKey] - authenticationProxy = getProxyForServerRole('authentication') + identitiesProxy = getProxyForServerRole('identities') return self.isAdmin() or \ - authenticationProxy.setContainsUser(translatorsSet) + identitiesProxy.setContainsUser(translatorsSet) def getLanguagesForObjectId(self, objectId): #languages = self.getPossibleLanguages() @@ -365,7 +365,7 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin, else: translatorsSet = None if not self.isAdmin() \ - and not getProxyForServerRole('authentication' + and not getProxyForServerRole('identities' ).setContainsUser(translatorsSet): raise faults.UserAccessDenied() digestsAndLabels = [] @@ -398,7 +398,7 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin, else: translatorsSet = None if not self.isAdmin() \ - and not getProxyForServerRole('authentication' + and not getProxyForServerRole('identities' ).setContainsUser(translatorsSet): raise faults.UserAccessDenied() destinationLanguage = localizationKey[2:] @@ -721,7 +721,7 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin, localizationKeys = [] for localizationKey, translatorsSet in \ virtualServer.admin.translatorsSets.items(): - if getProxyForServerRole('authentication').setContainsUser( + if getProxyForServerRole('identities').setContainsUser( translatorsSet): localizationKeys.append(localizationKey) return localizationKeys @@ -759,7 +759,7 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin, else: translatorsSet = None if not self.isAdmin() \ - and not getProxyForServerRole('authentication' + and not getProxyForServerRole('identities' ).setContainsUser(translatorsSet): raise faults.UserAccessDenied() assert translation.sourceLanguage == localization.sourceLanguage @@ -772,7 +772,7 @@ class TranslationsServer(TranslationsCommonMixin, AdministrableServerMixin, translation.setDestinationString( localization.destinationLanguage, localization.isTranslatable, localization.destinationString, localization.isFuzzy) - userId = getProxyForServerRole('authentication').getUserId() + userId = getProxyForServerRole('identities').getUserId() translation.addTranslator(localization.destinationLanguage, userId) if not translation.destinationStrings and not translation.sources: del virtualServer.translations[sign] diff --git a/servers/UploadFilesServer/UploadFilesServer.py b/servers/UploadFilesServer/UploadFilesServer.py index 38263c8b..ed1e140a 100755 --- a/servers/UploadFilesServer/UploadFilesServer.py +++ b/servers/UploadFilesServer/UploadFilesServer.py @@ -236,9 +236,9 @@ class UploadFilesServer(UploadFilesCommonMixin, ObjectsServer): if mimeType[0] is not None: object.dataType = mimeType[0] if not self.isAdmin() and not ( - getProxyForServerRole('authentication').setContainsUser( + getProxyForServerRole('identities').setContainsUser( self.getAdminCore().writersSet) - and getProxyForServerRole('authentication').setContainsUser( + and getProxyForServerRole('identities').setContainsUser( object.writersSet)): if not object.canBeCreatedByClient(): raise faults.UserAccessDenied() @@ -401,7 +401,7 @@ class UploadFilesServer(UploadFilesCommonMixin, ObjectsServer): if not self.canModifyObject(object.id) or not ( self.isAdmin() or not objectChanges.hasSlotName('writersSet') - or getProxyForServerRole('authentication' + or getProxyForServerRole('identities' ).setContainsUser( objectChanges.getSlot('writersSet').getValue())): if not object.canBeModifiedByClient(): diff --git a/servers/VirtualHostsServer/VirtualHostsServer.py b/servers/VirtualHostsServer/VirtualHostsServer.py index 9bb74168..4eaa89b6 100755 --- a/servers/VirtualHostsServer/VirtualHostsServer.py +++ b/servers/VirtualHostsServer/VirtualHostsServer.py @@ -526,7 +526,7 @@ class VirtualHostsServer(VirtualHostsCommonMixin, ObjectsServer): except IOError: pass - def upgrade_0001_0025(self, virtualServer): + def upgradeVirtualServer_0001_0025(self, virtualServer): # Repair dissociated objects and objectsByHostName virtualServer.objectsByHostName = {} for object in virtualServer.objects.values(): @@ -534,7 +534,7 @@ class VirtualHostsServer(VirtualHostsCommonMixin, ObjectsServer): virtualServer.objectsByHostName[object.hostName] = object virtualServer.markCoreAsDirty() - def upgrade_0001_0027(self, virtualServer): + def upgradeVirtualServer_0001_0027(self, virtualServer): for object in virtualServer.objects.values(): object.profiles = ['basic', 'cms', 'vote', 'translations'] virtualServer.markCoreAsDirty() diff --git a/servers/X509AccountsServer/X509AccountsServer.py b/servers/X509AccountsServer/X509AccountsServer.py new file mode 100755 index 00000000..69ef5970 --- /dev/null +++ b/servers/X509AccountsServer/X509AccountsServer.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 X509account Accounts Server""" + +__version__ = '$Revision$'[11:-2] + + +import copy +import sys + +glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install +sys.path.insert(0, glasnostPythonDir) + +import glasnost + +import glasnost.common.context as context +import glasnost.common.faults as faults +from glasnost.common.tools import iso8859_15 +import glasnost.common.tools_new as commonTool +import glasnost.common.X509AccountsCommon as commonX509Accounts + +import glasnost.server.ObjectsServer as objects + +from glasnost.proxy.tools import getProxy + + +applicationName = 'X509AccountsServer' +applicationRole = 'x509accounts' +dispatcher = None + + +class AdminX509Accounts(objects.AdminServerMixin, + commonX509Accounts.AdminX509Accounts): + pass +objects.register(AdminX509Accounts) + + +class X509Account(objects.ObjectServerMixin, + commonX509Accounts.X509Account): + def checkModifyIsPossible(self, changes, givenSlotNames = None): + objects.ObjectServerMixin.checkModifyIsPossible( + self, changes, givenSlotNames = givenSlotNames) + virtualServerId = context.getVar('applicationId') + virtualServer = self.getServer().getVirtualServer(virtualServerId) + if (not givenSlotNames or 'serial' in givenSlotNames) \ + and changes.serial != self.serial \ + and changes.serial is not None: + if virtualServer.objectsBySerial.has_key(changes.serial) \ + and changes.id != virtualServer.objectsBySerial[ + changes.serial].id: + raise faults.DuplicateSerial(changes.serial) + + def clear(self): + objectsBySerial = self.getServer().virtualServer.objectsBySerial + if objectsBySerial.has_key(self.serial): + del objectsBySerial[self.serial] + + def modify(self, changes, givenSlotNames = None): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getServer().getVirtualServer(virtualServerId) + serial = self.serial + objects.ObjectServerMixin.modify( + self, changes, givenSlotNames = givenSlotNames) + if self.serial != serial: + if serial is not None: + del virtualServer.objectsBySerial[serial] + if self.serial is not None: + virtualServer.objectsBySerial[self.serial] = self +objects.register(X509Account) + + +class X509AccountsVirtualServer(objects.ObjectsVirtualServer): + objectsBySerial = None + + def init(self): + objects.ObjectsVirtualServer.init(self) + self.objectsBySerial = {} + + +class X509AccountsServer( + commonX509Accounts.X509AccountsCommonMixin, + objects.ObjectsServer): + VirtualServer = X509AccountsVirtualServer + + def addObjectXmlRpc(self, objectImport): + objectId = objects.ObjectsServer.addObjectXmlRpc(self, objectImport) + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + object = virtualServer.loadObjectCore(objectId) + if virtualServer.objectsBySerial.has_key(object.serial): + # Serial already used. + del virtualServer.objects[objectId] + virtualServer.markObjectAsDeleted(objectId) + virtualServer.markCoreAsDirty() + raise faults.DuplicateSerial(object.serial) + + virtualServer.objectsBySerial[object.serial] = object + virtualServer.markCoreAsDirty() + return objectId + + def checkObjectAuthenticationXmlRpc(self, serial): + virtualServerId = context.getVar('applicationId') + virtualServer = self.getVirtualServer(virtualServerId) + if not virtualServer.objectsBySerial.has_key(serial): + raise faults.WrongX509Serial(serial) + object = virtualServer.objectsBySerial[serial] + identitiesProxy = getProxy(object.identityId) + return identitiesProxy.getUserToken(object.identityId) + + def registerPublicMethods(self): + objects.ObjectsServer.registerPublicMethods(self) + self.registerPublicMethod('checkObjectAuthentication', + self.checkObjectAuthenticationXmlRpc) + + +x509AccountsServer = X509AccountsServer() + + +if __name__ == "__main__": + x509AccountsServer.launch(applicationName, applicationRole) diff --git a/shared/common/ArticlesCommon.py b/shared/common/ArticlesCommon.py index d05901de..79f4d414 100644 --- a/shared/common/ArticlesCommon.py +++ b/shared/common/ArticlesCommon.py @@ -95,7 +95,7 @@ class ArticleCommon(ObjectCommon): lastEditorId = None lastEditorId_kind_importExport = 'from-server-only' - lastEditorId_kind_serverRoles = ['people'] + lastEditorId_kind_serverRoles = ['identities'] lastEditorId_kindName = 'Id' modificationTime = None diff --git a/shared/common/CardsCommon.py b/shared/common/CardsCommon.py index 0af93cb2..717c494e 100644 --- a/shared/common/CardsCommon.py +++ b/shared/common/CardsCommon.py @@ -183,7 +183,7 @@ class CardCommon(ObjectCommon): try: proxyGroups.getSetContainedIds( self.getSlot('readersSet').getValue(), - serverRoles = ['people'], + serverRoles = ['identities'], raiseWhenUncountable = 1) except faults.UncountableGroup: # Even non identified users can read the object. We can cache @@ -364,6 +364,11 @@ class CardCommon(ObjectCommon): return property.kind return None + def getEmail(self): + if not self.hasSlotName('email'): + return None + return self.getSlot('email') + # This method has ben commented, because it doesn't work when a property name # has changed: self.values may contain an item whose name doesn't correspond to # a property anymore. @@ -374,6 +379,11 @@ class CardCommon(ObjectCommon): ## return slotNames ## return slotNames + self.values.keys() + def getFingerprint(self): + if not self.hasSlotName('fingerprint'): + return None + return self.getSlot('fingerprint') + def getImportSlotNames(self, parentSlot = None): names = ObjectCommon.getImportSlotNames(self, parentSlot = parentSlot) names = names[:] @@ -416,9 +426,15 @@ class CardCommon(ObjectCommon): return 'Card number %s' % commonTools.extractLocalId(self.id) def getLabelLanguage(self): - if hasattr(self, 'language') and self.language: - return self.language - return '' + language = self.getLanguage() + if language is None: + language = '' + return language + + def getLanguage(self): + if not self.hasSlotName('language'): + return None + return self.getSlot('language') def getMode(self, modeName, parentSlot = None): if modeName == 'edit': @@ -561,6 +577,14 @@ class CardCommon(ObjectCommon): return ObjectCommon.importFromXmlRpc( self, dataImport, parentSlot = parentSlot) + def mustCryptEmails(self): + if not self.hasSlotName('cryptEmails'): + return 0 + if self.getSlot('cryptEmails'): + return 1 + else: + return 0 + def setDirectPropertyValue(self, propertyName, value): if self.values is None: self.values = {} diff --git a/shared/common/ElectionsCommon.py b/shared/common/ElectionsCommon.py index e92d23d8..b0771d92 100644 --- a/shared/common/ElectionsCommon.py +++ b/shared/common/ElectionsCommon.py @@ -205,7 +205,7 @@ class AbstractElectionCommon(ObjectCommon): def getVoterIds(self, virtualServerId = None): from glasnost.proxy.GroupsProxy import getSetContainedIds - return getSetContainedIds(self.votersSet, ['people']) + return getSetContainedIds(self.votersSet, ['identities']) def newVote(self): from glasnost.proxy.VotesProxy import votesProxy diff --git a/shared/common/GradesCommon.py b/shared/common/GradesCommon.py index 5fc05a0a..92503389 100644 --- a/shared/common/GradesCommon.py +++ b/shared/common/GradesCommon.py @@ -58,7 +58,7 @@ class GradeCommon(ObjectCommon): marks_kindName = 'Marks' membersSet = None - membersSet_kind_itemKind_value_serverRoles = ['people'] + membersSet_kind_itemKind_value_serverRoles = ['identities'] membersSet_kind_itemKind_valueName = 'Id' membersSet_kind_minCount = 1 membersSet_kindName = 'Sequence' diff --git a/shared/common/GroupsCommon.py b/shared/common/GroupsCommon.py index d761010f..ec644f10 100644 --- a/shared/common/GroupsCommon.py +++ b/shared/common/GroupsCommon.py @@ -127,8 +127,7 @@ class GroupAbstract(objects.ObjectCommon): if groupId == system.generalPublicId: if results.has_key(groupId): return results[groupId] - if not serverRoles or 'people' in serverRoles or \ - 'ldappeople' in serverRoles: + if not serverRoles or 'identities' in serverRoles: smallAndLarge = 'uncountable', 'uncountable' else: smallAndLarge = [], [] @@ -154,9 +153,8 @@ class GroupAbstract(objects.ObjectCommon): if groupId == system.generalPublicId: if results.has_key(groupId): return results[groupId] - result = not objectId or \ - commonTools.extractRole(objectId) in \ - ('people', 'ldappeople') + result = not objectId \ + or commonTools.extractRole(objectId) == 'identities' results[groupId] = result return result else: diff --git a/shared/common/IdentitiesCommon.py b/shared/common/IdentitiesCommon.py new file mode 100644 index 00000000..b54de6b0 --- /dev/null +++ b/shared/common/IdentitiesCommon.py @@ -0,0 +1,188 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Common Models""" + +__version__ = '$Revision$'[11:-2] + + +import faults +import ObjectsCommon as objects +import things +import tools_new as commonTools + + +class AdminIdentities(objects.AdminCommon): + serverRole = 'identities' + + +class Identification(things.BaseThing): + localNameIdentifier = None + class localNameIdentifier_kindClass: + _kindName = 'String' # FIXME: NameIdentifier or Token or... + # isRequired = 1 FIXME removed for CVQ demo only. Uncomment ASAP. + + peerHostName = None + class peerHostName_kindClass: + _kindName = 'String' # FIXME. + # isRequired = 1 FIXME removed for CVQ demo only. Uncomment ASAP. + + peerNameIdentifier = None + class peerNameIdentifier_kindClass: + _kindName = 'String' # FIXME: NameIdentifier or Token or... + # isRequired = 1 FIXME removed for CVQ demo only. Uncomment ASAP. +things.register(Identification) + + +class Identity(objects.ObjectCommon): + identityIdentifications = None + class identityIdentifications_kindClass: + _kindName = 'Sequence' + label = N_('Identity Providers Identifications') + class itemKind_valueClass: + _kindName = 'Thing' + valueThingCategory = 'other' + valueThingName = 'Identification' + stateInEditMode = 'hidden' + stateInViewMode = 'hidden' + + language_kindName = None + + objectsMemory = None + class objectsMemory_kindClass: + _kindName = 'Memory' + importExport = 'from-server-only' + stateInEditMode = 'hidden' + stateInViewMode = 'hidden' + + personId = None + class personId_kindClass: + _kindName = 'Id' + # isRequired = 1 FIXME: uncomment later + label = N_('Person') + serverRoles = ['people'] + + serverRole = 'identities' + + serviceIdentifications = None + class serviceIdentifications_kindClass: + _kindName = 'Sequence' + label = N_('Service Providers Identifications') + class itemKind_valueClass: + _kindName = 'Thing' + valueThingCategory = 'other' + valueThingName = 'Identification' + stateInEditMode = 'hidden' + stateInViewMode = 'hidden' + + spellcheckEntries = 1 + class spellcheckEntries_kindClass: + _kindName = 'Boolean' + balloonHelp = N_('Should Glasnost spellcheck your texts?') + isRequired = 1 + label = N_('Spellcheck Entries') + + voteTokens = None + class voteTokens_kindClass: + _kindName = 'Mapping' + importExport = 'from-server-only' + class keyKind_valueClass: + _kindName = 'Id' + serverRoles = ['elections'] + stateInEditMode = 'hidden' + stateInViewMode = 'hidden' + class valueKind_valueClass: + _kindName = 'Token' + + def getEmail(self): + person = self.getPerson() + if person is None: + return None + return person.getEmail() + + def getFingerprint(self): + person = self.getPerson() + if person is None: + return None + return person.getFingerprint() + + def getLabel(self): + person = self.getPerson() + if person is None: + return '#%s' % commonTools.extractLocalId(self.id) + return person.getLabel() + + def getLanguage(self): + person = self.getPerson() + if person is None: + return None + return person.getLanguage() + + def getPerson(self): + if self.personId is not None: + from glasnost.proxy.tools import getProxy + peopleWeb = getProxy(self.personId) + if peopleWeb is None: + return None + try: + return peopleWeb.getObject(self.personId) + except faults.MissingItem: + pass + return None + + def mustCryptEmails(self): + person = self.getPerson() + if person is None: + return 0 + return person.mustCryptEmails() + + +class IdentitiesCommonMixin(objects.ObjectsCommonMixin): + adminClassName = 'AdminIdentities' + newObjectNameCapitalized = N_('New Identity') + objectClassName = 'Identity' + objectName = N_('identity') + objectNameCapitalized = N_('Identity') + objectsName = N_('identities') + objectsNameCapitalized = N_('Identities') + serverRole = 'identities' + diff --git a/shared/common/ObjectsCommon.py b/shared/common/ObjectsCommon.py index 13d4f0e6..4564d0dd 100644 --- a/shared/common/ObjectsCommon.py +++ b/shared/common/ObjectsCommon.py @@ -80,7 +80,7 @@ class ObjectCommon(things.BaseThing): import glasnost.proxy.GroupsProxy as proxyGroups try: proxyGroups.getSetContainedIds( - self.readersSet, serverRoles = ['people'], + self.readersSet, serverRoles = ['identities'], raiseWhenUncountable = 1) except faults.UncountableGroup: # Even non identified users can read the object. We can cache diff --git a/shared/common/PasswordAccountsCommon.py b/shared/common/PasswordAccountsCommon.py new file mode 100644 index 00000000..a61b1a38 --- /dev/null +++ b/shared/common/PasswordAccountsCommon.py @@ -0,0 +1,136 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Password Accounts Common Models""" + +__version__ = '$Revision$'[11:-2] + + +import ObjectsCommon as objects + + +class AdminPasswordAccounts(objects.AdminCommon): + serverRole = 'passwordaccounts' + + storePasswordsInClearText = 1 + class storePasswordsInClearText_kindClass: + _kindName = 'Boolean' + isRequired = 1 + label = N_('Password Type') + labels = { + '0': N_('Clear Text'), + '1': N_('Crypted'), + } + + userCanChoosePassword = 0 + class userCanChoosePassword_kindClass: + _kindName = 'Boolean' + balloonHelp = N_( + 'Specifies whether users are allowed to choose ' + 'their passwords (instead of autogenerating one for them).') + isRequired = 1 + label = N_('Password') + labels = { + '0': N_('Automatically Generated'), + '1': N_('User Choice'), + } + + def getOrderedLayoutSlotNames(self, parentSlot = None): + slotNames = objects.AdminCommon.getOrderedLayoutSlotNames( + self, parentSlot = parentSlot) + slotNames += ['userCanChoosePassword', 'storePasswordsInClearText'] + return slotNames + + +class PasswordAccount(objects.ObjectCommon): + language_kindName = None + + login = None + class login_kindClass: + _kindName = 'String' + balloonHelp = N_('Enter the username you use on this site.') + isRequired = 1 + isTranslatable = 0 + label = N_('Username') + textMaxLength = 40 + widget_size = 15 + + password = None + class password_kindClass: + _kindName = 'Password' + balloonHelp = N_('Enter your secret password.') + isRequired = 1 + label = N_('Password') + textMaxLength = 15 + widget_size = 15 + + identityId = None + class identityId_kindClass: + _kindName = 'Id' + isRequired = 1 + label = N_('Identity') + serverRoles = ['identities'] + serverRole = 'passwordaccounts' + + def getLabel(self): + label = self.login + if label is None: + return '' + return label + + def getOrderedLayoutSlotNames(self, parentSlot = None): + slotNames = objects.ObjectCommon.getOrderedLayoutSlotNames( + self, parentSlot = parentSlot) + slotNames += ['login', 'password', 'identityId'] + return slotNames + + +class PasswordAccountsCommonMixin(objects.ObjectsCommonMixin): + adminClassName = 'AdminPasswordAccounts' + newObjectNameCapitalized = N_('New Password Account') + objectClassName = 'PasswordAccount' + objectName = N_('password account') + objectNameCapitalized = N_('Password Account') + objectsName = N_('password accounts') + objectsNameCapitalized = N_('Password Accounts') + serverRole = 'passwordaccounts' + diff --git a/shared/common/PeopleCommon.py b/shared/common/PeopleCommon.py index 4090eb0b..47357304 100644 --- a/shared/common/PeopleCommon.py +++ b/shared/common/PeopleCommon.py @@ -74,6 +74,17 @@ class PersonCommon(ObjectCommon): creationTime = None creationTime_kindName = 'CreationTime' + cryptEmails = 0 + class cryptEmails_kindClass: + _kindName = 'Boolean' + balloonHelp = N_('Should Glasnost encrypt your emails?') + isRequired = 1 + label = N_('Crypt Emails') + labels = { + '0': N_('Never'), + '1': N_('When Possible'), + } + email = None email_kind_balloonHelp = N_('Enter the e-mail address.') email_kind_isRequired = 1 @@ -92,8 +103,6 @@ class PersonCommon(ObjectCommon): firstName_kind_isRequired = 1 firstName_kindName = 'String' - language_kindName = None - lastName = None lastName_kind_balloonHelp = N_('Enter the last name.') lastName_kind_isTranslatable = 0 @@ -110,13 +119,6 @@ class PersonCommon(ObjectCommon): serverRole = 'people' - voteTokens = None - voteTokens_kind_importExport = 'from-server-only' - voteTokens_kind_keyKind_valueName = 'Id' - voteTokens_kind_keyKind_value_serverRoles = ['elections'] - voteTokens_kind_valueKind_valueName = 'Token' - voteTokens_kindName = 'Mapping' - def canCache(self): return 0 @@ -157,6 +159,12 @@ class PersonCommon(ObjectCommon): return 1 return 0 + def getEmail(self): + return self.email + + def getFingerprint(self): + return self.fingerprint + def getFullName(self): """Return the full person name string. @@ -180,14 +188,6 @@ class PersonCommon(ObjectCommon): return self.email return label - def getLabelLanguage(self): - """Return an empty string.""" - return '' - - def getLanguage(self): - """Return an empty string.""" - return '' - def getOrderedLayoutSlotNames(self, parentSlot = None): """Return the layout slots names sequences. @@ -227,6 +227,9 @@ class PersonCommon(ObjectCommon): sortString += ' ' + self.firstName return sortString + def mustCryptEmails(self): + return self.cryptEmails + class PeopleCommonMixin(ObjectsCommonMixin): diff --git a/shared/common/PreferencesCommon.py b/shared/common/PreferencesCommon.py index 1ac1a6da..ea2add94 100644 --- a/shared/common/PreferencesCommon.py +++ b/shared/common/PreferencesCommon.py @@ -92,10 +92,6 @@ class PreferenceCommon(ObjectCommon): 'sv', ] - objectsMemory = None - objectsMemory_kind_importExport = 'from-server-only' - objectsMemory_kindName = 'Memory' - serverRole = 'preferences' spellcheckEntries = 0 @@ -107,7 +103,7 @@ class PreferenceCommon(ObjectCommon): def getOrderedLayoutSlotNames(self, parentSlot = None): slotNames = ObjectCommon.getOrderedLayoutSlotNames( self, parentSlot = parentSlot) - slotNames += ['currency', 'cryptEmails', 'spellcheckEntries', 'objectsMemory'] + slotNames += ['currency', 'cryptEmails', 'spellcheckEntries'] return slotNames diff --git a/shared/common/ProvidersCommon.py b/shared/common/ProvidersCommon.py new file mode 100644 index 00000000..1d673f48 --- /dev/null +++ b/shared/common/ProvidersCommon.py @@ -0,0 +1,180 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Providers Common Models""" + +__version__ = '$Revision$'[11:-2] + + +import ObjectsCommon as objects + + +class AdminProviders(objects.AdminCommon): + remoteIdentityProviderId = None + class remoteIdentityProviderId_kindClass: + _kindName = 'Id' + label = N_('Remote Identity Provider') + serverRoles = ['providers'] + # FIXME: To do: Restrict to identity providers ids. Don't accept + # service providers ids. + + serverRole = 'providers' + + +class ProviderAbstract(objects.ObjectCommon): + # The URL to which the provider redirects at the end of user-agent-based + # Federation Termination Notification Protocol profiles. + federationTerminationServiceReturnUrl = None + class federationTerminationServiceReturnUrl_kindClass: + _kindName = 'String' # FIXME: url + + # The URL Used for user-agent-based Federation Termination Notification + # Protocol profiles. + federationTerminationServiceUrl = None + class federationTerminationServiceUrl_kindClass: + _kindName = 'String' # FIXME: url + + # The providerID of the entity. + # Example http://serviceprovider.com + providerId = None + class providerId_kindClass: + _kindName = 'String' # FIXME: uri + isRequired = 1 + + # The provider s redirecting URL for use after HTTP name registration has + # taken place. + registerNameIdentifierServiceReturnUrl = None + class registerNameIdentifierServiceReturnUrl_kindClass: + _kindName = 'String' # FIXME: uri + + # The URL used for user-agent-based Register Name Identifier Protocol + # profiles. + registerNameIdentifierServiceUrl = None + class registerNameIdentifierServiceUrl_kindClass: + _kindName = 'String' # FIXME: uri + + # The provider's preferred Register Name Identifier Protocol profile, + # which should be used by other providers when registering a new + # identifier. Each element MUST contain a valid Register Name Identifier + # Protocol profile identification URI. The absence of this element SHALL + # mean that the provider does not support any profile of the Register Name + #Identifier Protocol. + registerNameIdentifierProtocolProfiles = None + class registerNameIdentifierProtocolProfiles_kindClass: + _kindName = 'Sequence' + class itemKind_valueClass: + _kindName = 'String' # FIXME: uri + + serverRole = 'providers' + + # The Single Logout Protocol profiles supported by the provider. Each + # element MUST contain a valid Single Logout Protocol profile + # identification URI. The absence of this element SHALL mean that the + # provider does not support any profile of the Single Logout Protocol. + singleLogoutProtocolProfiles = None + class singleLogoutProtocolProfiles_kindClass: + _kindName = 'Sequence' + class itemKind_valueClass: + _kindName = 'String' # FIXME: uri + + # The URL to which the provider redirects at the end of user-agent-based + # Single Logout Protocol profiles. + singleLogoutServiceReturnUrl = None + class singleLogoutServiceReturnUrl_kindClass: + _kindName = 'String' # FIXME: url + + # The URL Used for user-agent-based Single Logout Protocol profiles. + singleLogoutServiceUrl = None + class singleLogoutServiceUrl_kindClass: + _kindName = 'String' # FIXME: url + + # The provider SOAP endpoint URI. + soapEndpoint = None + class soapEndpoint_kindClass: + _kindName = 'String' # FIXME: uri + isRequired = 1 + + def getLabel(self): + label = self.providerId + if label is None: + return '' + return label + + +class IdentityProvider(ProviderAbstract): + # The Single Sign-On Protocol profiles supported by the provider. Each + # element MUST contain a valid Single Sign-On Protocol profile + # identification URI. + singleSignOnProtocolProfiles = None + class singleSignOnProtocolProfiles_kindClass: + _kindName = 'Sequence' + class itemKind_valueClass: + _kindName = 'String' # FIXME: uri + + # The identity provider's URL for accepting authentication requests for the + # Single Sign-On and Federation Protocol. + singleSignOnServiceUrl = None + class singleSignOnServiceUrl_kindClass: + _kindName = 'String' + isRequired = 1 + + +class ServiceProvider(ProviderAbstract): + # URI of the SP for receiving Authentication Assertions from an + # authenticating party. + # FIXME: Liberty Alliance allows several AssertionConsumerServiceURLs. + assertionConsumerServiceUrl = None + class assertionConsumerServiceUrl_kindClass: + _kindName = 'String' + isRequired = 1 + + +class ProvidersCommonMixin(objects.ObjectsCommonMixin): + adminClassName = 'AdminProviders' + newObjectNameCapitalized = N_('New Provider') + objectClassName = 'IdentityProvider' + objectName = N_('provider') + objectNameCapitalized = N_('Provider') + objectsName = N_('providers') + objectsNameCapitalized = N_('Providers') + serverRole = 'providers' + diff --git a/shared/common/VirtualHostsCommon.py b/shared/common/VirtualHostsCommon.py index daadf4b2..136607a4 100644 --- a/shared/common/VirtualHostsCommon.py +++ b/shared/common/VirtualHostsCommon.py @@ -201,7 +201,9 @@ class VirtualHostCommon(ObjectCommon): widgetName = 'MultiCheck' readersSet = None + # FIXME: Which of the following lines is correct? readersSet_defaultValue = [system.generalPublicId] + # readersSet_kind_itemKind_value_defaultValue = system.generalPublicId readersSet_kindName = 'ReadersSet' serverRole = 'virtualhosts' diff --git a/shared/common/VotesCommon.py b/shared/common/VotesCommon.py index 957bb98c..0be259cf 100644 --- a/shared/common/VotesCommon.py +++ b/shared/common/VotesCommon.py @@ -77,7 +77,7 @@ class AbstractVoteCommon(ObjectCommon): token_kindName = 'Token' voterId_kind_importExport = 'private' - voterId_kind_serverRoles = ['people'] + voterId_kind_serverRoles = ['identities'] voterId_kindName = 'Id' voterToken = None diff --git a/shared/common/X509AccountsCommon.py b/shared/common/X509AccountsCommon.py new file mode 100644 index 00000000..a4935f15 --- /dev/null +++ b/shared/common/X509AccountsCommon.py @@ -0,0 +1,99 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 X509 Accounts Common Models""" + +__version__ = '$Revision$'[11:-2] + + +import ObjectsCommon as objects + + +class AdminX509Accounts(objects.AdminCommon): + serverRole = 'x509accounts' + + +class X509Account(objects.ObjectCommon): + language_kindName = None + + serial = None + class serial_kindClass: + _kindName = 'String' + balloonHelp = N_('Enter the serial you use on this site.') + isRequired = 1 + isTranslatable = 0 + label = N_('Serial') + textMaxLength = 40 + widget_size = 15 + + identityId = None + class identityId_kindClass: + _kindName = 'Id' + isRequired = 1 + label = N_('Identity') + serverRoles = ['identities'] + + serverRole = 'x509accounts' + + def getLabel(self): + label = self.serial + if label is None: + return '' + return label + + def getOrderedLayoutSlotNames(self, parentSlot = None): + slotNames = objects.ObjectCommon.getOrderedLayoutSlotNames( + self, parentSlot = parentSlot) + slotNames += ['serial', 'identityId'] + return slotNames + + +class X509AccountsCommonMixin(objects.ObjectsCommonMixin): + adminClassName = 'AdminX509Accounts' + newObjectNameCapitalized = N_('New X509v3 Account') + objectClassName = 'X509Account' + objectName = N_('X509v3 account') + objectNameCapitalized = N_('X509v3 Account') + objectsName = N_('X509v3 accounts') + objectsNameCapitalized = N_('X509v3 Accounts') + serverRole = 'x509accounts' + diff --git a/shared/common/XhtmlGenerator.py b/shared/common/XhtmlGenerator.py index 434ca01e..06814208 100755 --- a/shared/common/XhtmlGenerator.py +++ b/shared/common/XhtmlGenerator.py @@ -479,8 +479,16 @@ class httpUrl(urlCommon): relativeUrl = urllib.quote(self.getPath(**keywords)) if self.parameters: relativeUrl = '%s;%s' % (relativeUrl, self.parameters) - query = '&'.join([ '%s=%s' % (name, urllib.quote(str(value))) - for name, value in self.getArguments()]) + expandedArguments = [] + for name, value in self.getArguments(): + if type(value) in [types.ListType, types.TupleType]: + for item in value: + expandedArguments.append( + '%s=%s' % (name, urllib.quote(str(item)))) + else: + expandedArguments.append( + '%s=%s' % (name, urllib.quote(str(value)))) + query = '&'.join(expandedArguments) if self.query and query: query = '%s&%s' % (self.query, query) elif self.query: diff --git a/shared/common/faults.py b/shared/common/faults.py index 5b738950..b33dcb83 100644 --- a/shared/common/faults.py +++ b/shared/common/faults.py @@ -4,7 +4,7 @@ # Glasnost # By: Odile Bénassy # Romain Chantereau -# Nicolas Clapiès +# Nicolas Clapiès # Pierre-Antoine Dejace # Thierry Dulieu # Florent Monnier @@ -94,6 +94,10 @@ faultCodeValueTooBig = 41 faultCodeValueTooSmall = 42 faultCodeRoleNotInProfiles = 43 faultCodeUnknownObjectVersion = 44 +faultCodeWrongArtifact = 45 +faultCodeUnableToChangePasswordStorage = 46 +faultCodeWrongNameIdentifier = 47 +faultCodeWrongX509Serial = 48 faultCodeUnknownVoteToken = 1000 faultCodeUnknownVoterToken = 1001 @@ -448,6 +452,35 @@ class UnknownCommandAction(BaseFault): return 'Unknown command action: "%s"' % action +class WrongArtifact(BaseFault): + faultCode = faultCodeWrongArtifact + + def makeFaultString(self, artifact): + return 'Unknown artifact = %s' % artifact + + +class UnableToChangePasswordStorage(BaseFault): + faultCode = faultCodeUnableToChangePasswordStorage + + def makeFaultString(self): + return 'Unable to change password storage' + + +class WrongNameIdentifier(BaseFault): + faultCode = faultCodeWrongNameIdentifier + + def makeFaultString(self, nameIdentifier): + return 'Unknown name identifier = %s' % nameIdentifier + + +class WrongX509Serial(BaseFault): + faultCode = faultCodeWrongX509Serial + uiFaultString = N_('Wrong value!') + + def makeFaultString(self, serial): + return 'Unknown serial = "%s"' % serial + + # Vote diff --git a/shared/common/kinds.py b/shared/common/kinds.py index b6075ef8..2b1cf739 100644 --- a/shared/common/kinds.py +++ b/shared/common/kinds.py @@ -784,6 +784,24 @@ class BaseKind(things.BaseThing): defaultValue = self.getDefaultValue(slot) if defaultValue is not None: slot.setValue(defaultValue) + + def upgradeModel(self, slot, model, toVersion): + changed = 0 + # Call upgradeModel_xxx methods. + classes = commonTools.getC3ClassLinearization(self.__class__) + upgradeMethodNames = [] + for class_ in classes: + for attributeName in class_.__dict__.keys(): + if attributeName.startswith('upgradeModel_'): + if not attributeName in upgradeMethodNames: + upgradeMethodNames.append(attributeName) + upgradeMethodNames.sort() + for upgradeMethodName in upgradeMethodNames: + if upgradeMethodName[len('upgradeModel_'):] <= toVersion: + continue + modelChanged, model = getattr(self, upgradeMethodName)(slot, model) + changed = changed or modelChanged + return changed, model register(BaseKind) @@ -922,6 +940,19 @@ class AbstractSequence(BaseKind): itemKind = itemSlot.getKind() itemKind.setToDefaultValue(slot) + def upgradeModel(self, slot, model, toVersion): + changed, model = BaseKind.upgradeModel(self, slot, model, toVersion) + # Upgrade each item. + if model is not None: + for i, item in enumerate(model): + itemSlot = self.getItemSlot(slot, i) + itemChanged, item = itemSlot.getKind().upgradeModel( + itemSlot, item, toVersion) + if itemChanged: + changed = 1 + model[i] = item + return changed, model + class Alias(BaseKind): defaultValue_kindName = 'Alias' @@ -1633,6 +1664,19 @@ class Id(BaseKind): if toVersion == 4000: return repairId(value) return None + + def upgradeModel_0001_0028(self, slot, model): + """Convert user ids from "people" to "identities".""" + + changed = 0 + if self.serverRoles is not None and 'identities' in serverRoles \ + and not 'people' in serverRoles \ + and commonTools.extractRole(model) == 'people': + changed = 1 + model = '%s/%s' % ( + commonTools.makeApplicationId(model, 'identities'), + commonTools.extractLocalId(model)) + return changed, model register(Id) @@ -2136,6 +2180,28 @@ class Mapping(BaseKind): if changed: return value return None + + def upgradeModel(self, slot, model, toVersion): + changed, model = BaseKind.upgradeModel(self, slot, model, toVersion) + # Upgrade each key and value. + if model is not None: + # The .keys() below is required, even for Python >= 2.3, because + # the for loop modifies the dict, while iterating inside. + for keyIndex, key in enumerate(model.keys()): + keySlot = self.getItemKeySlot(slot, keyIndex) + keyChanged, upgradedKey = keySlot.getKind().upgradeModel( + keySlot, key, toVersion) + if keyChanged: + changed = 1 + model[upgradedKey] = model.pop(key) + for key, value in model.items(): + valueSlot = self.getItemValueSlot(slot, key) + valueChanged, value = valueSlot.getKind().upgradeModel( + valueSlot, value, toVersion) + if valueChanged: + changed = 1 + model[key] = value + return changed, model register(Mapping) @@ -2722,7 +2788,7 @@ class UsersSet(Sequence): itemKind_kind_stateInViewMode = 'hidden' itemKind_value_permanentIds = [system.generalPublicId, system.loggedUsersGroupId] - itemKind_value_serverRoles = ['groups', 'people', 'ldappeople'] + itemKind_value_serverRoles = ['groups', 'identities'] itemKind_valueName = 'Id' label = N_('Users') @@ -2734,14 +2800,18 @@ class UsersSet(Sequence): thingPublicName = N_('Users') def getDefaultValue(self, slot): - return [context.getVar('userId')] + userId = context.getVar('userId') + if userId: + return [userId] + else: + return None register(UsersSet) class AuthorsSet(UsersSet): balloonHelp = N_('Choose the author(s) for this object.') - containerNames = ['Any', 'Sequence', 'UsersSet'] + containerNames = ['Any', 'Sequence'] defaultValue_kindName = 'AuthorsSet' @@ -2750,10 +2820,18 @@ class AuthorsSet(UsersSet): # FIXME: that's useless since the user may "visit" the group and it will # be shown nevertheless itemKind_value_permanentIds = None + itemKind_value_serverRoles = ['groups', 'people'] label = N_('Authors') thingPublicName = N_('Authors') + + def getDefaultValue(self, slot): + user = context.getVar('user') + if user is not None and user.personId is not None: + return [user.personId] + else: + return None register(AuthorsSet) @@ -2837,7 +2915,7 @@ register(Properties) class ReadersSet(UsersSet): balloonHelp = N_( - 'Select the people and groups who are allowed to read the item.') + 'Select the identities and groups who are allowed to read the item.') containerNames = ['Any', 'Sequence', 'UsersSet'] @@ -3326,7 +3404,12 @@ class Thing(Choice): else: BaseKind.setToDefaultValue(self, slot) - + def upgradeModel(self, slot, model, toVersion): + changed, model = BaseKind.upgradeModel(self, slot, model, toVersion) + if model is not None: + if model.upgrade(toVersion, parentSlot = slot): + changed = 1 + return changed, model register(Thing) @@ -3531,7 +3614,7 @@ register(TranslatorsSets) class WritersSet(UsersSet): balloonHelp = N_( - 'Select the people and groups who are allowed to modify the item.') + 'Select the identities and groups who are allowed to modify the item.') containerNames = ['Any', 'Sequence', 'UsersSet'] diff --git a/shared/common/slots.py b/shared/common/slots.py index 9560c27f..b608ff93 100644 --- a/shared/common/slots.py +++ b/shared/common/slots.py @@ -181,7 +181,7 @@ class BaseSlot: def getObject(self): if self.container is not None and \ - self.container.thingCategory in ('object', 'authentication'): + self.container.thingCategory == 'object': return self.container elif self.parent is not None: return self.parent.getObject() @@ -190,7 +190,7 @@ class BaseSlot: def getObjectSlot(self): if self.container is not None and \ - self.container.thingCategory in ('object', 'authentication'): + self.container.thingCategory == 'object': return self.parent elif self.parent is not None: return self.parent.getObjectSlot() diff --git a/shared/common/things.py b/shared/common/things.py index e7524003..85e26280 100644 --- a/shared/common/things.py +++ b/shared/common/things.py @@ -549,3 +549,35 @@ class BaseThing: def saveNonCore(self, objectDirectoryPath, parentSlot = None): pass + def upgrade(self, toVersion, parentSlot = None): + changed = 0 + + # Call object upgrade_xxx methods. + classes = commonTools.getC3ClassLinearization(self.__class__) + upgradeMethodNames = [] + for class_ in classes: + for attributeName in class_.__dict__.keys(): + if attributeName.startswith('upgrade_'): + if not attributeName in upgradeMethodNames: + upgradeMethodNames.append(attributeName) + upgradeMethodNames.sort() + for upgradeMethodName in upgradeMethodNames: + if upgradeMethodName[len('upgrade_'):] <= toVersion: + continue + changed = getattr(self, upgradeMethodName)( + parentSlot = parentSlot) or changed + + # Upgrade each object slot. + slotNames = self.getSlotNames(parentSlot = parentSlot) + for slotName, value in self.__dict__.items(): + if not slotName in slotNames: + continue + slot = self.getSlot(slotName, parentSlot = parentSlot) + valueChanged, value = slot.getKind().upgradeModel( + slot, value, toVersion) + if valueChanged: + changed = 1 + slot.setValue(value) + + return changed + diff --git a/shared/common/tools.py b/shared/common/tools.py index 789f8d50..8a1efc64 100644 --- a/shared/common/tools.py +++ b/shared/common/tools.py @@ -67,7 +67,6 @@ except ImportError: import faults import context - import tools_new as commonTools is8bit = re.compile("[\x80-\xff]").search @@ -273,9 +272,7 @@ def sendMail(mailFrom, mailTo, mailSubject, mailMessage, mailPerson = None, The message string to send. *mailPerson*: - If the Person object has GPG abilities, the Person object has to be - gived in order to test if the mail have to be crypted. - Default: None. + The recipient's person or identity. *moreHearders*: If other hearders have to be included in the mail, this dictionnary @@ -306,16 +303,13 @@ def sendMail(mailFrom, mailTo, mailSubject, mailMessage, mailPerson = None, mailMessage = "\n" + mailMessage gpgEncrypted = 0 - if mailPerson: - from glasnost.proxy.tools import getProxyForServerRole - preferencesProxy = getProxyForServerRole('preferences') - cryptEmails = preferencesProxy.getPreferenceByUserId( - mailPerson.id).cryptEmails + if mailIdentity: + cryptEmails = mailPerson.mustCryptEmails() if cryptEmails: try: from gpg import Gpg - gpg = Gpg(email = mailPerson.email, - fingerprint = mailPerson.fingerprint) + gpg = Gpg(email = mailPerson.getEmail(), + fingerprint = mailPerson.getFingerprint()) gpg.deleteKey() gpg.addKey() mailMessage = gpg.encrypt(mailMessage) diff --git a/shared/common/xhtmlgenerator.py b/shared/common/xhtmlgenerator.py index f5025384..6a8170d7 100644 --- a/shared/common/xhtmlgenerator.py +++ b/shared/common/xhtmlgenerator.py @@ -302,9 +302,9 @@ class menuIds: objectIds = acceptedObjectIds self.menus[role] = getObjectLabelsTranslated( objectIds, context.getVar('readLanguages')) - preferences = context.getVar('preferences') - if preferences: - objectsMemory = preferences.objectsMemory + user = context.getVar('user') + if user: + objectsMemory = user.objectsMemory if objectsMemory is not None: for role in self.roles: if not objectsMemory.has_key(role): diff --git a/shared/gtk/IdentitiesGtk.py b/shared/gtk/IdentitiesGtk.py new file mode 100644 index 00000000..df75c111 --- /dev/null +++ b/shared/gtk/IdentitiesGtk.py @@ -0,0 +1,66 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Gtk""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.proxy.IdentitiesProxy as proxyIdentities + +import ObjectsGtk as objects +import things + + +class Identification(things.ThingMixin, proxyIdentities.Identification): + pass +things.register(Identification) + + +class Identity(objects.ObjectGtkMixin, proxyIdentities.Identity): + pass +objects.register(Identity) + + +class IdentitiesGtk(objects.ObjectsGtkMixin, proxyIdentities.IdentitiesProxy): + pass + diff --git a/shared/gtk/PasswordAccountsGtk.py b/shared/gtk/PasswordAccountsGtk.py new file mode 100644 index 00000000..b0182fe9 --- /dev/null +++ b/shared/gtk/PasswordAccountsGtk.py @@ -0,0 +1,62 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Password Accounts Gtk""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.proxy.PasswordAccountsProxy as proxyPasswordAccounts + +import ObjectsGtk as objects + + +class PasswordAccount(objects.ObjectGtkMixin, + proxyPasswordAccounts.PasswordAccount): + pass +objects.register(PasswordAccount) + + +class PasswordAccountsGtk(objects.ObjectsGtkMixin, + proxyPasswordAccounts.PasswordAccountsProxy): + pass + diff --git a/shared/gtk/ProvidersGtk.py b/shared/gtk/ProvidersGtk.py new file mode 100644 index 00000000..03f1bb22 --- /dev/null +++ b/shared/gtk/ProvidersGtk.py @@ -0,0 +1,67 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Providers Gtk""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.proxy.ProvidersProxy as proxyProviders + +import ObjectsGtk as objects + + +class IdentityProvider(objects.ObjectGtkMixin, + proxyProviders.IdentityProvider): + pass +objects.register(IdentityProvider) + + +class ServiceProvider(objects.ObjectGtkMixin, + proxyProviders.ServiceProvider): + pass +objects.register(ServiceProvider) + + +class ProvidersGtk(objects.ObjectsGtkMixin, proxyProviders.ProvidersProxy): + pass + diff --git a/shared/proxy/AssertionsProxy.py b/shared/proxy/AssertionsProxy.py new file mode 100644 index 00000000..0dfe5a13 --- /dev/null +++ b/shared/proxy/AssertionsProxy.py @@ -0,0 +1,72 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Assertions Proxy""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.common.context as context + +from DispatcherProxy import callServer, getApplicationToken +import ObjectsProxy as objects + + +class AssertionsProxy(objects.Proxy): + serverRole = 'assertions' + + def addAssertion(self, assertion, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'addAssertion', + [serverId, getApplicationToken(), userToken, assertion]) + + def getAssertion(self, artifact, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'getAssertion', + [serverId, getApplicationToken(), userToken, artifact]) + diff --git a/shared/proxy/IdentitiesProxy.py b/shared/proxy/IdentitiesProxy.py new file mode 100644 index 00000000..2bc16a37 --- /dev/null +++ b/shared/proxy/IdentitiesProxy.py @@ -0,0 +1,169 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Proxy""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.common.context as context +import glasnost.common.IdentitiesCommon as commonIdentities +import glasnost.common.tools_new as commonTools + +from DispatcherProxy import callServer, getApplicationToken +import ObjectsProxy as objects +import things + + +class AdminIdentities(objects.AdminMixin, commonIdentities.AdminIdentities): + pass +objects.register(AdminIdentities) + + +class Identification(things.ThingMixin, commonIdentities.Identification): + pass +things.register(Identification) + + +class Identity(objects.ObjectProxyMixin, commonIdentities.Identity): + pass +objects.register(Identity) + + +class IdentitiesProxy(commonIdentities.IdentitiesCommonMixin, + objects.ObjectsProxy): + def abstainForVote(self, electionId): + userToken = context.getVar('userToken', default = '') + serverId = self.computeServerIdFromUserToken(userToken) + callServer( + serverId, + 'abstainForVote', + [serverId, getApplicationToken(), userToken, electionId]) + + def checkIdentityLocalNameIdentifier( + self, peerHostName, localNameIdentifier, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'checkIdentityLocalNameIdentifier', + [serverId, getApplicationToken(), userToken, peerHostName, + localNameIdentifier]) + + def checkIdentityPeerNameIdentifier( + self, peerHostName, peerNameIdentifier, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'checkIdentityPeerNameIdentifier', + [serverId, getApplicationToken(), userToken, peerHostName, + peerNameIdentifier]) + + def computeServerIdFromUserToken(self, userToken): + return commonTools.makeApplicationId(userToken, self.serverRole) + + def deleteUserToken(self): + userToken = context.getVar('userToken', default = '') + if not userToken: + return + serverId = self.computeServerIdFromUserToken(userToken) + callServer( + serverId, + 'deleteUserToken', + [serverId, getApplicationToken(), userToken]) + + def getElectionVoteToken(self, electionId): + userToken = context.getVar('userToken', default = '') + serverId = self.computeServerIdFromUserToken(userToken) + return callServer( + serverId, + 'getElectionVoteToken', + [serverId, getApplicationToken(), userToken, electionId]) + + def getUserId(self): + userToken = context.getVar('userToken', default = '') + if not userToken: + return '' + serverId = self.computeServerIdFromUserToken(userToken) + return callServer( + serverId, + 'getUserId', + [serverId, getApplicationToken(), userToken]) + + def getUserToken(self, id): + userToken = '' + serverId = commonTools.extractServerId(id) + return callServer( + serverId, + 'getUserToken', + [serverId, getApplicationToken(), userToken, id]) + + def rememberId(self, id, serverId = None): + userToken = context.getVar('userToken', default = '') + if not userToken: + return + serverId = self.computeServerIdFromUserToken(userToken) + callServer( + serverId, + 'rememberId', + [serverId, getApplicationToken(), userToken, id]) + + def setContainsUser(self, set, serverId = None): + userToken = context.getVar('userToken', default = '') + if set is None: + return 0 + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'setContainsUser', + [serverId, getApplicationToken(), userToken, set]) + + def vote(self, electionId, voteToken): + userToken = context.getVar('userToken', default = '') + serverId = self.computeServerIdFromUserToken(userToken) + callServer( + serverId, + 'vote', + [serverId, getApplicationToken(), userToken, electionId, + voteToken]) + diff --git a/shared/proxy/PasswordAccountsProxy.py b/shared/proxy/PasswordAccountsProxy.py new file mode 100644 index 00000000..9ef140c9 --- /dev/null +++ b/shared/proxy/PasswordAccountsProxy.py @@ -0,0 +1,86 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Password Accounts Proxy""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.common.context as context +import glasnost.common.PasswordAccountsCommon as commonPasswordAccounts + +from DispatcherProxy import callServer, getApplicationToken +import ObjectsProxy as objects +from tools import utf8 + + +class AdminPasswordAccounts(objects.AdminMixin, + commonPasswordAccounts.AdminPasswordAccounts): + pass +objects.register(AdminPasswordAccounts) + + +class PasswordAccount(objects.ObjectProxyMixin, + commonPasswordAccounts.PasswordAccount): + pass +objects.register(PasswordAccount) + + +class PasswordAccountsProxy(commonPasswordAccounts.PasswordAccountsCommonMixin, + objects.ObjectsProxy): + def checkObjectAuthentication(self, login, password, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + if login is None: + loginExport = '' + else: + loginExport = utf8(login) + if password is None: + passwordExport = '' + else: + passwordExport = utf8(password) + return callServer( + serverId, + 'checkObjectAuthentication', + [serverId, getApplicationToken(), userToken, loginExport, + passwordExport]) + diff --git a/shared/proxy/PeopleProxy.py b/shared/proxy/PeopleProxy.py index 02bf04b5..21b061b4 100644 --- a/shared/proxy/PeopleProxy.py +++ b/shared/proxy/PeopleProxy.py @@ -81,135 +81,8 @@ register(Person) class PeopleProxy(PeopleCommonMixin, ObjectsProxy): - - """Proxy of the People server. - - This class is used in order to communicate with the server. - - Attributes: - ========== - - *adminClassName*: - The Administrator wrapper people class name. - - *newObjectNameCapitalized*: - The gettext string illustrating a new user. - - *objectClassName*: - The normal people wrapper class name. - - *objectName*: - The gettext string of the people object name. - - *objectNameCapitalized*: - The gettext string of the people object name, capitalzed. - - *objectsName*: - The gettext string of the collection of people objects. - - *objectsNameCapitalized*: - The gettext string of the collection of people objects, capitalized. - - *serverRole*: - The name of the server. - - """ - - def abstainForVote(self, electionId): - """Abstain voting on an election. - - Keyword arguments: - ================== - - *virtualServerId*: - The virtual server ID string where are the elections. - - *clientToken*: - The client application token. - - *userToken*: - The user token. - - *electionId*: - The Id of the election to abstain. - - Return 0 everytime. - - Exceptions: - =========== - - *faults.MissingItem*: - The specified election does not exist on the specified server. - - *AttributeError*: - No authentication proxy found. - - """ - - userToken = context.getVar('userToken', default = '') - serverId = self.computeServerIdFromUserToken(userToken) - callServer( - serverId, - 'abstainForVote', - [serverId, getApplicationToken(), userToken, electionId]) - - def computeServerIdFromUserToken(self, userToken): - """Return the user's server ID string from his token. - - Keyword argument: - ================= - - *userToken*: - The well formed user token. - - *Exception*: - ============ - - *Exception*: - The *userToken* is malformed. - The string has no fields separated by ':'. - - """ - - return 'glasnost://%s/%s' % ( - extractApplicationHostNameAndPort(userToken), - self.serverRole) - def findObjectIds(self, text, serverId = None): - """Find person objects ID corresponding to a specified string. - - Keyword arguments: - ================== - - *text*: - The text that must be in the person object (conforming to the - object containstext method). - - *serverId*: - The server ID (default: None). - - Return values: - ============== - - *The corresponding objects IDs sequence* - - Exceptions: - =========== - - *KeyError*: - The virtual server ID does not correspond to a instanciated virtual - server. - - *AssertionError*: - The given server ID doesn't begin by 'glasnost://'. - - *Standard Exception*: - The server ID folowing the protocol specifier is not valid. - - *Exception*('Missing context'): - The context does not contain information about the dispatcher Id. - - """ + """Find people IDs corresponding to a specified string.""" userToken = context.getVar('userToken', default = '') serverId = self.getServerId(serverId = serverId) @@ -220,46 +93,3 @@ class PeopleProxy(PeopleCommonMixin, ObjectsProxy): 'findObjectIds', [serverId, getApplicationToken(), userToken, utf8(text)]) - def getElectionVoteToken(self, electionId): - userToken = context.getVar('userToken', default = '') - serverId = self.computeServerIdFromUserToken(userToken) - return callServer( - serverId, - 'getElectionVoteToken', - [serverId, getApplicationToken(), userToken, electionId]) - - def vote(self, electionId, voteToken): - """Vote to an election. - - Keyword arguments: - ================== - - *electionId*: - The ID of the election to vote. - - *voteToken*: - The vote token associated to the vote. - - *Return 0.* - =========== - - Exceptions: - =========== - - *faults.MissingItem*: - The specified election was not found. - - *KeyError*: - The virtual server ID does not correspond to a instanciated virtual - server. - - """ - - userToken = context.getVar('userToken', default = '') - serverId = self.computeServerIdFromUserToken(userToken) - callServer( - serverId, - 'vote', - [serverId, getApplicationToken(), userToken, electionId, - voteToken]) - diff --git a/shared/proxy/PreferencesProxy.py b/shared/proxy/PreferencesProxy.py index 289abf32..8512904c 100644 --- a/shared/proxy/PreferencesProxy.py +++ b/shared/proxy/PreferencesProxy.py @@ -84,14 +84,6 @@ class PreferencesProxy(PreferencesCommonMixin, Proxy): 'getObjectByUserId', [serverId, getApplicationToken(), userToken, userId]) return commonTools.importThing(objectImport) - - def rememberId(self, id, serverId = None): - userToken = context.getVar('userToken', default = '') - serverId = self.getServerId(serverId = serverId) - callServer( - serverId, - 'rememberId', - [serverId, getApplicationToken(), userToken, id]) def setPreference(self, object, serverId = None): userToken = context.getVar('userToken', default = '') diff --git a/shared/proxy/ProvidersProxy.py b/shared/proxy/ProvidersProxy.py new file mode 100644 index 00000000..2f6e1ef7 --- /dev/null +++ b/shared/proxy/ProvidersProxy.py @@ -0,0 +1,88 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Providers Proxy""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.common.context as context +import glasnost.common.ProvidersCommon as commonProviders + +from DispatcherProxy import callServer, getApplicationToken +import ObjectsProxy as objects + + +class AdminProviders(objects.AdminMixin, commonProviders.AdminProviders): + pass +objects.register(AdminProviders) + + +class IdentityProvider(objects.ObjectProxyMixin, + commonProviders.IdentityProvider): + pass +objects.register(IdentityProvider) + + +class ServiceProvider(objects.ObjectProxyMixin, + commonProviders.ServiceProvider): + pass +objects.register(ServiceProvider) + + +class ProvidersProxy(commonProviders.ProvidersCommonMixin, + objects.ObjectsProxy): + def getRemoteIdentityProviderId(self, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'getRemoteIdentityProviderId', + [serverId, getApplicationToken(), userToken]) + + def getServiceProviderId(self, providerId, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'getServiceProviderId', + [serverId, getApplicationToken(), userToken, providerId]) diff --git a/shared/proxy/SessionsProxy.py b/shared/proxy/SessionsProxy.py index dcb6a513..34658b33 100644 --- a/shared/proxy/SessionsProxy.py +++ b/shared/proxy/SessionsProxy.py @@ -55,7 +55,7 @@ class SessionsProxy(Proxy): serverRole = 'sessions' def addTemporaryData(self, data, serverId = None): - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') sessionToken = context.getVar('sessionToken') serverId = self.getServerId(serverId = serverId) return callServer( @@ -65,7 +65,7 @@ class SessionsProxy(Proxy): sessionToken, data]) def delTemporaryData(self, dataToken, serverId = None): - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') sessionToken = context.getVar('sessionToken') serverId = self.getServerId(serverId = serverId) callServer( @@ -75,7 +75,7 @@ class SessionsProxy(Proxy): sessionToken, dataToken]) def getTemporaryData(self, dataToken, serverId = None): - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') sessionToken = context.getVar('sessionToken') serverId = self.getServerId(serverId = serverId) return callServer( @@ -85,7 +85,7 @@ class SessionsProxy(Proxy): sessionToken, dataToken]) def deleteSession(self, objectToken, serverId = None): - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') serverId = self.getServerId(serverId = serverId) callServer( serverId, @@ -93,7 +93,7 @@ class SessionsProxy(Proxy): [serverId, getApplicationToken(), userToken, objectToken]) def getHistory(self, serverId = None): - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') serverId = self.getServerId(serverId = serverId) return callServer( serverId, @@ -101,7 +101,7 @@ class SessionsProxy(Proxy): [serverId, getApplicationToken(), userToken]) def getSession(self, objectToken, ipAddress, serverId = None): - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') serverId = self.getServerId(serverId = serverId) object = callServer( serverId, @@ -111,7 +111,7 @@ class SessionsProxy(Proxy): return object def newSession(self, ipAddress, serverId = None): - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') serverId = self.getServerId(serverId = serverId) return callServer( serverId, @@ -130,7 +130,7 @@ class SessionsProxy(Proxy): if not object.has_key('isDirty'): return del object['isDirty'] - userToken = context.getVar('userToken') + userToken = context.getVar('userToken', default = '') serverId = self.getServerId(serverId = serverId) object['version'] = callServer( serverId, diff --git a/shared/proxy/X509AccountsProxy.py b/shared/proxy/X509AccountsProxy.py new file mode 100644 index 00000000..a55b20e7 --- /dev/null +++ b/shared/proxy/X509AccountsProxy.py @@ -0,0 +1,75 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 X509 Accounts Proxy""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.common.context as context +import glasnost.common.X509AccountsCommon as commonX509Accounts + +from DispatcherProxy import callServer, getApplicationToken +import ObjectsProxy as objects + + +class AdminX509Accounts(objects.AdminMixin, + commonX509Accounts.AdminX509Accounts): + pass +objects.register(AdminX509Accounts) + + +class X509Account(objects.ObjectProxyMixin, commonX509Accounts.X509Account): + pass +objects.register(X509Account) + + +class X509AccountsProxy(commonX509Accounts.X509AccountsCommonMixin, + objects.ObjectsProxy): + def checkObjectAuthentication(self, serial, serverId = None): + userToken = context.getVar('userToken', default = '') + serverId = self.getServerId(serverId = serverId) + return callServer( + serverId, + 'checkObjectAuthentication', + [serverId, getApplicationToken(), userToken, serial]) + diff --git a/shared/server/ObjectsServer.py b/shared/server/ObjectsServer.py index b56b4b5c..68c4cecb 100644 --- a/shared/server/ObjectsServer.py +++ b/shared/server/ObjectsServer.py @@ -726,12 +726,12 @@ class ObjectsVirtualServer(AdministrableVirtualServer): return object = self.loadObjectCore(objectId) object.acquireNonCore() - data = { - 'time': time.time(), - 'userId': getProxyForServerRole('authentication').getUserId(), - 'object': object - } try: + data = { + 'time': time.time(), + 'userId': getProxyForServerRole('identities').getUserId(), + 'object': object, + } if not os.access(self.dataDirectoryPath, os.F_OK): os.mkdir(self.dataDirectoryPath) os.chmod(self.dataDirectoryPath, 0750) @@ -837,8 +837,11 @@ class Server(things.BaseThing, applications.Application): return if self.hasMultipleVirtualServers: - self.virtualServers[newVirtualServerId] = self.loadVirtualServer( - newVirtualServerId) + newVirtualServer = self.initVirtualServer(newVirtualServerId) + self.virtualServers[newVirtualServerId] = newVirtualServer + newVirtualServer = self.loadVirtualServer(newVirtualServer) + self.virtualServers[newVirtualServerId] = newVirtualServer + context.push( applicationId = newVirtualServerId, ) @@ -860,12 +863,13 @@ class Server(things.BaseThing, applications.Application): upgradeMethodNames = [] for class_ in classes: for attributeName in class_.__dict__.keys(): - if attributeName.startswith('upgrade_'): + if attributeName.startswith('upgradeVirtualServer_'): if not attributeName in upgradeMethodNames: upgradeMethodNames.append(attributeName) upgradeMethodNames.sort() if len(upgradeMethodNames) > 0: - lastMethodVersionNumber = upgradeMethodNames[-1][len('upgrade_'):] + lastMethodVersionNumber = upgradeMethodNames[-1][ + len('upgradeVirtualServer_'):] if lastMethodVersionNumber > glasnost.fileVersionNumber: raise Exception( 'The constant fileVersionNumber in shared/__init__' @@ -922,6 +926,9 @@ class Server(things.BaseThing, applications.Application): virtualServer.exportNonCore(exportDirectoryPath) return None + def fillEmptyVirtualServer(self, virtualServer): + pass + def getVirtualServer(self, virtualServerId): """Return the Virtal server instance. @@ -1116,7 +1123,9 @@ class Server(things.BaseThing, applications.Application): elif os.access(pickleFilePath, os.F_OK): import cPickle as glasnostPickle rcFile = open(pickleFilePath, 'rb') - if rcFile is not None: + if rcFile is None: + self.fillEmptyVirtualServer(virtualServer) + else: fcntl.lockf(rcFile, fcntl.LOCK_SH) version = self.readFileVersion(rcFile) #print 'file:', rcFile @@ -1146,6 +1155,20 @@ class Server(things.BaseThing, applications.Application): originalContext.setVar('server', self) originalContext.setVar('serverRole', self.applicationRole) + def initVirtualServer(self, virtualServerId): + assert virtualServerId is not None + dispatcherId = commonTools.extractDispatcherId(virtualServerId) + context.push( + applicationId = virtualServerId, + dispatcherId = dispatcherId, + ) + try: + virtualServer = self.VirtualServer() + virtualServer.init() + finally: + context.pull() + return virtualServer + def launch(self, applicationName, applicationRole): self.applicationName = applicationName self.applicationRole = applicationRole @@ -1173,11 +1196,14 @@ class Server(things.BaseThing, applications.Application): if dispatcherId is not None: virtualServerId = commonTools.makeApplicationId( dispatcherId, self.applicationRole) - self.virtualServers[virtualServerId] = self.loadVirtualServer( - virtualServerId) + virtualServer = self.initVirtualServer(virtualServerId) + self.virtualServers[virtualServerId] = virtualServer + virtualServer = self.loadVirtualServer(virtualServer) + self.virtualServers[virtualServerId] = virtualServer else: virtualServerId = context.getVar('applicationId') - self.virtualServer = self.loadVirtualServer(virtualServerId) + self.virtualServer = self.initVirtualServer(virtualServerId) + self.virtualServer = self.loadVirtualServer(self.virtualServer) def loadConfigOptions(self): applications.Application.loadConfigOptions(self) @@ -1202,20 +1228,15 @@ class Server(things.BaseThing, applications.Application): self.dataDirectoryPath = commonTools.getConfig( 'Misc', 'DataDirectoryPath') - def loadVirtualServer(self, virtualServerId): - assert virtualServerId is not None - dispatcherId = commonTools.extractDispatcherId(virtualServerId) + def loadVirtualServer(self, virtualServer): context.push( - applicationId = virtualServerId, - dispatcherId = dispatcherId, - emptyVirtualServer = None, + applicationId = virtualServer.virtualServerId, + dispatcherId = commonTools.extractDispatcherId( + virtualServer.virtualServerId), + emptyVirtualServer = virtualServer, ) - try: - virtualServer = self.VirtualServer() - virtualServer.init() - context.setVar('emptyVirtualServer', virtualServer) - - if self.useDataFile: + if self.useDataFile: + try: pickleFilePath = os.path.join( virtualServer.dataDirectoryPath, self.applicationName + '.pickle') @@ -1230,7 +1251,9 @@ class Server(things.BaseThing, applications.Application): elif os.access(pickleFilePath, os.F_OK): import cPickle as glasnostPickle rcFile = open(pickleFilePath, 'rb') - if rcFile is not None: + if rcFile is None: + self.fillEmptyVirtualServer(virtualServer) + else: fcntl.lockf(rcFile, fcntl.LOCK_SH) version = self.readFileVersion(rcFile) print 'file:', rcFile @@ -1247,11 +1270,11 @@ class Server(things.BaseThing, applications.Application): virtualServer.initFromOldData(data) virtualServer.isReadOnly = isReadOnly self.upgradeVirtualServer(virtualServer, version) - finally: - context.pull() + finally: + context.pull() return virtualServer - def publicMethodWrapper(self, method, arguments): + def publicMethodWrapper(self, method, arguments, isDirectCall = 0): """The standard wrapper for public methods. Most Glasnost public methods use the same 3 parameters: @@ -1271,13 +1294,14 @@ class Server(things.BaseThing, applications.Application): return apply(method, arguments) finally: # Save all dirty data. - virtualServerId = context.getVar('applicationId') - try: - virtualServer = self.getVirtualServer(virtualServerId) - except faults.UnknownServerId: - pass - else: - virtualServer.savePendingRequests() + if not isDirectCall: + virtualServerId = context.getVar('applicationId') + try: + virtualServer = self.getVirtualServer(virtualServerId) + except faults.UnknownServerId: + pass + else: + virtualServer.savePendingRequests() self.pullContext() @@ -1390,8 +1414,10 @@ class Server(things.BaseThing, applications.Application): self.hostName, self.port, virtualServerId) except faults.RoleNotInProfiles: continue - self.virtualServers[virtualServerId] = self.loadVirtualServer( - virtualServerId) + virtualServer = self.initVirtualServer(virtualServerId) + self.virtualServers[virtualServerId] = virtualServer + virtualServer = self.loadVirtualServer(virtualServer) + self.virtualServers[virtualServerId] = virtualServer context.push( applicationId = virtualServerId, ) @@ -1491,12 +1517,13 @@ class Server(things.BaseThing, applications.Application): upgradeMethodNames = [] for class_ in classes: for attributeName in class_.__dict__.keys(): - if attributeName.startswith('upgrade_'): + if attributeName.startswith('upgradeVirtualServer_'): if not attributeName in upgradeMethodNames: upgradeMethodNames.append(attributeName) upgradeMethodNames.sort() for upgradeMethodName in upgradeMethodNames: - if upgradeMethodName[len('upgrade_'):] <= fileVersionNumber: + if upgradeMethodName[len('upgradeVirtualServer_'):] \ + <= fileVersionNumber: continue getattr(self, upgradeMethodName)(virtualServer) virtualServer.savePendingRequests() @@ -1606,7 +1633,7 @@ class AdministrableServerMixin: admin = virtualServer.admin if context.getVar('isAdmin') is not None: return context.getVar('isAdmin') - result = getProxyForServerRole('authentication').setContainsUser( + result = getProxyForServerRole('identities').setContainsUser( admin.adminsSet) context.setVar('isAdmin', result) return result @@ -1710,20 +1737,24 @@ class AdministrableServerMixin: virtualServer.markAdminAsDirty(admin) invalidateValue('admin_%s' % self.serverRole) - def upgrade_0001_0019(self, virtualServer): + def upgradeVirtualServer_0001_0019(self, virtualServer): # Fill empty adminsSet slots in admin. admin = virtualServer.admin if admin.hasSlotName('adminsSet') and not admin.adminsSet: admin.adminsSet = [system.generalPublicId] virtualServer.markAdminAsDirty(admin) - def upgrade_0001_0020(self, virtualServer): + def upgradeVirtualServer_0001_0020(self, virtualServer): # Fill empty readersSet slots in admin. admin = virtualServer.admin if admin.hasSlotName('readersSet') and not admin.readersSet: admin.readersSet = [system.generalPublicId] virtualServer.markAdminAsDirty(admin) + def upgradeVirtualServer_0001_0028(self, virtualServer): + # Convert user ids from "people" to "identities". + if virtualServer.admin.upgrade('0001_0028'): + virtualServer.markAdminAsDirty(admin) class ObjectsServer(AdministrableServerMixin, Server): @@ -1797,7 +1828,7 @@ class ObjectsServer(AdministrableServerMixin, Server): if not self.isAdmin() and not (self.canAddObject() and ( not object.hasSlotName('writersSet') - or getProxyForServerRole('authentication' + or getProxyForServerRole('identities' ).setContainsUser(object.getSlot('writersSet' ).getValue()))): if not object.canBeCreatedByClient(): @@ -1823,7 +1854,7 @@ class ObjectsServer(AdministrableServerMixin, Server): admin = self.getAdminCore() except faults.UserAccessDenied: return 0 - return getProxyForServerRole('authentication').setContainsUser( + return getProxyForServerRole('identities').setContainsUser( admin.writersSet) def canDeleteObject(self, objectId): @@ -1836,7 +1867,7 @@ class ObjectsServer(AdministrableServerMixin, Server): object = virtualServer.loadObjectCore(objectId) return self.isAdmin() or ( object.hasSlotName('writersSet') - and getProxyForServerRole('authentication').setContainsUser( + and getProxyForServerRole('identities').setContainsUser( object.getSlot('writersSet').getValue())) def canGetObject(self, objectId): @@ -1856,7 +1887,7 @@ class ObjectsServer(AdministrableServerMixin, Server): pass if object.hasSlotName('readersSet'): readersSet = object.getSlot('readersSet').getValue() - return getProxyForServerRole('authentication').setContainsUser( + return getProxyForServerRole('identities').setContainsUser( readersSet) else: if not self.useAdminReadersSet: @@ -1865,7 +1896,7 @@ class ObjectsServer(AdministrableServerMixin, Server): admin = self.getAdminCore() except faults.UserAccessDenied: return 0 - return getProxyForServerRole('authentication').setContainsUser( + return getProxyForServerRole('identities').setContainsUser( admin.readersSet) def canGetObjects(self): @@ -1879,7 +1910,7 @@ class ObjectsServer(AdministrableServerMixin, Server): admin = self.getAdminCore() except faults.UserAccessDenied: return 0 - if getProxyForServerRole('authentication').setContainsUser( + if getProxyForServerRole('identities').setContainsUser( admin.readersSet): return 1 return 0 @@ -1896,7 +1927,7 @@ class ObjectsServer(AdministrableServerMixin, Server): return 0 return self.isAdmin() or ( object.hasSlotName('writersSet') - and getProxyForServerRole('authentication').setContainsUser( + and getProxyForServerRole('identities').setContainsUser( object.getSlot('writersSet').getValue())) def canUseObject(self, objectId): @@ -1922,7 +1953,7 @@ class ObjectsServer(AdministrableServerMixin, Server): if object.hasSlotName('usersSet'): usersSet = object.getSlot('usersSet').getValue() return not usersSet or getProxyForServerRole( - 'authentication').setContainsUser(usersSet) + 'identities').setContainsUser(usersSet) else: return 1 @@ -2073,12 +2104,14 @@ class ObjectsServer(AdministrableServerMixin, Server): isAdmin = self.isAdmin() try: possibleReaderIds = getSetContainedIds( - possibleReadersSet, ['people'], raiseWhenUncountable = 1) + possibleReadersSet, ['identities'], + raiseWhenUncountable = 1) except faults.UncountableGroup: possibleReaderIds = 'everybody' try: possibleWriterIds = getSetContainedIds( - possibleWritersSet, ['people'], raiseWhenUncountable = 1) + possibleWritersSet, ['identities'], + raiseWhenUncountable = 1) except faults.UncountableGroup: possibleWriterIds = 'everybody' objectIds = virtualServer.objects.keys() @@ -2099,7 +2132,7 @@ class ObjectsServer(AdministrableServerMixin, Server): hasReadersSet = 0 readersSet = None if not isAdmin and hasReadersSet and not getProxyForServerRole( - 'authentication').setContainsUser(readersSet): + 'identities').setContainsUser(readersSet): continue if not self.getLastObjectIds_filter( possibleReaderIds, hasReadersSet, readersSet): @@ -2124,7 +2157,7 @@ class ObjectsServer(AdministrableServerMixin, Server): if hasSet: try: ids = getSetContainedIds( - set, ['people'], raiseWhenUncountable = 1) + set, ['identities'], raiseWhenUncountable = 1) except faults.IllegalRecursiveGroup: # There is an error in the group. For security reasons, return # false. @@ -2507,7 +2540,7 @@ class ObjectsServer(AdministrableServerMixin, Server): if not self.canModifyObject(object.id) or not ( self.isAdmin() or not objectChanges.hasSlotName('writersSet') - or getProxyForServerRole('authentication').setContainsUser( + or getProxyForServerRole('identities').setContainsUser( objectChanges.getSlot('writersSet').getValue())): if not object.canBeModifiedByClient(): raise faults.UserAccessDenied() @@ -2585,7 +2618,7 @@ class ObjectsServer(AdministrableServerMixin, Server): if not self.canModifyObject(object.id) or not ( self.isAdmin() or not objectChanges.hasSlotName('writersSet') - or getProxyForServerRole('authentication').setContainsUser( + or getProxyForServerRole('identities').setContainsUser( objectChanges.getSlot('writersSet').getValue())): if not object.canBeModifiedByClient(): raise faults.UserAccessDenied() @@ -2980,8 +3013,9 @@ class ObjectsServer(AdministrableServerMixin, Server): self.rpcServer.remove_subscription( when, virtualServerId, method, clientId, callBackName) - def upgrade_0001_0019(self, virtualServer): - AdministrableServerMixin.upgrade_0001_0019(self, virtualServer) + def upgradeVirtualServer_0001_0019(self, virtualServer): + AdministrableServerMixin.upgradeVirtualServer_0001_0019( + self, virtualServer) # In each object, fill the readersSet slot when it exists and is empty. for id, object in virtualServer.objects.items(): @@ -2989,3 +3023,12 @@ class ObjectsServer(AdministrableServerMixin, Server): object.readersSet = [system.generalPublicId] virtualServer.markObjectAsDirty(object) + def upgradeVirtualServer_0001_0028(self, virtualServer): + AdministrableServerMixin.upgradeVirtualServer_0001_0028( + self, virtualServer) + + # Convert user ids from "people" to "identities". + for object in virtualServer.objects.values(): + if object.upgrade('0001_0028'): + virtualServer.markObjectAsDirty(object) + diff --git a/shared/server/xmlrpcServer.py b/shared/server/xmlrpcServer.py index 7105a846..0b697e47 100644 --- a/shared/server/xmlrpcServer.py +++ b/shared/server/xmlrpcServer.py @@ -290,7 +290,7 @@ class ServerMixin: context.initFromOther(self.baseContext) currentContext = context.get() if useWrapper and self._wrapper is not None: - result = self._wrapper(method, params) + result = self._wrapper(method, params, isDirectCall) else: result = apply(method, params) if not isDirectCall: diff --git a/shared/web/AuthenticationLoginPasswordWeb.py b/shared/web/AuthenticationLoginPasswordWeb.py index 34f622f1..8b56f88e 100644 --- a/shared/web/AuthenticationLoginPasswordWeb.py +++ b/shared/web/AuthenticationLoginPasswordWeb.py @@ -89,6 +89,7 @@ class AccountLoginPassword(BaseObjectWebMixin, AccountLoginPassword): return slotNames register(AccountLoginPassword) + class ChangingPassword(BaseObjectWebMixin, ObjectCommon): id_kindName = None language_kindName = None @@ -493,7 +494,6 @@ correctly configured.\ return writePageLayout(layout, _('Login')) finally: context.pull(_level = 'index') - login.isPublicForWeb = 1 def loginSubmit(self, **keywords): if keywords is None: @@ -541,6 +541,7 @@ correctly configured.\ authObject = self.newAuthenticationObject() return self.newAccountObject(userCardObject, authObject) + newAccount.isPublicForWeb = 1 def newAccountObject(self, userCardObject, authObject): userCardSlot = slots.Root(userCardObject, name = 'userCard') @@ -573,7 +574,6 @@ correctly configured.\ return writePageLayout(layout, _('New Account')) finally: context.pull(_level = 'index') - newAccount.isPublicForWeb = 1 def newAccountSubmit(self, **keywords): if keywords is None: diff --git a/shared/web/CardsWeb.py b/shared/web/CardsWeb.py index 4eca1eb8..d2b06f6c 100644 --- a/shared/web/CardsWeb.py +++ b/shared/web/CardsWeb.py @@ -506,8 +506,8 @@ class CardsWeb(ObjectsWebMixin, CardsProxy): if not object.hasMode(modeName): return pageNotFound() mode = object.getMode(modeName) - authenticationProxy = getProxyForServerRole('authentication') - if not authenticationProxy.setContainsUser( + identitiesProxy = getProxyForServerRole('identities') + if not identitiesProxy.setContainsUser( mode.getModelUsersSet(object)): return accessForbidden() @@ -675,8 +675,8 @@ class CardsWeb(ObjectsWebMixin, CardsProxy): object.prototypeIds = [prototypeId] object.__class__ = CardForUse # Important. mode = object.getMode(modeName) - authenticationProxy = getProxyForServerRole('authentication') - if not authenticationProxy.setContainsUser( + identitiesProxy = getProxyForServerRole('identities') + if not identitiesProxy.setContainsUser( mode.getModelUsersSet(object)): return accessForbidden() diff --git a/shared/web/ElectionsWeb.py b/shared/web/ElectionsWeb.py index 1008cf15..b01259b7 100644 --- a/shared/web/ElectionsWeb.py +++ b/shared/web/ElectionsWeb.py @@ -269,13 +269,12 @@ class ElectionMixin(ObjectWebMixin): def getVotesLayout(self): ballotsWeb = getWebForServerRole('ballots') gradesWeb = getWebForServerRole('grades') - peopleWeb = getWebForServerRole('people') layout = X.array() userVoteToken = None - userId = context.getVar('userId', default = '') - if userId: - user = peopleWeb.getObject(userId) + userId = context.getVar('userId') + user = context.getVar('user') + if user is not None: if user.voteTokens is not None \ and user.voteTokens.has_key(self.id): userVoteToken = user.voteTokens[self.id] @@ -727,15 +726,15 @@ class ElectionsWeb(ObjectsWebMixin, ElectionsProxy): layout += ObjectsWebMixin.getViewOtherActionButtonsBarLayout( self, object, fields) if object.state == 'running' \ - and getProxyForServerRole('authentication').setContainsUser( + and getProxyForServerRole('identities').setContainsUser( object.votersSet): layout += X.buttonStandalone( 'vote', X.roleUrl('votes', 'edit').add('electionId', object.id)) - authenticationProxy = getProxyForServerRole('authentication') + identitiesProxy = getProxyForServerRole('identities') if object.state == 'running' and \ (isAdmin or \ - authenticationProxy.setContainsUser(object.writersSet)): + identitiesProxy.setContainsUser(object.writersSet)): layout += X.buttonStandalone( 'pester-abstentionnists', X.idUrl(object.id, 'confirmPesterAbstentionnists')) @@ -821,7 +820,7 @@ class ElectionsWeb(ObjectsWebMixin, ElectionsProxy): if not self.canGetObjects(): return accessForbidden() isAdmin = self.isAdmin() - userId = context.getVar('userId', default = '') + userId = context.getVar('userId') if userId: userSet = [userId] else: diff --git a/shared/web/IdentitiesWeb.py b/shared/web/IdentitiesWeb.py new file mode 100644 index 00000000..c9458c94 --- /dev/null +++ b/shared/web/IdentitiesWeb.py @@ -0,0 +1,721 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 +from OpenSSL import SSL +import socket +import urlparse +import whrandom +from xml.dom.minidom import parseString + +import lasso.Protocols.SingleSignOnAndFederation as sso +from lasso.Schemas.SchemaDom import importFromNode +import lasso.Soap + +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 OK, accessForbidden, getWeb, getWebForServerRole, redirect, writePageLayout + + +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 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 login(self): # FIXME: Rename to signOn or authentication or ..., because some authentication methods require no password? + identityProviderId = None + providersWeb = getWebForServerRole('providers') + if providersWeb is not None: + try: + identityProviderId = providersWeb.getRemoteIdentityProviderId() + except faults.MissingItem: + pass + if identityProviderId is None: + # Don't use Liberty Alliance protocol to authenticate user. + return self.loginLocal(context.getVar('nextUri')) + identityProvider = providersWeb.getObject(identityProviderId) + singleSignOnServiceUrl = identityProvider.singleSignOnServiceUrl + loginUrl = sso.getIdentityProviderAuthenticationUrl( + authenticationMethods = ['softwarePki', 'password'], + profile = 'artifact', # FIXME: or 'post'. + relayState = context.getVar('nextUri'), # FIXME: To encrypt. + serviceProviderId = context.getVar('httpHostName'), + singleSignOnServiceUrl = singleSignOnServiceUrl) + return redirect(loginUrl) + login.isPublicForWeb = 1 + + def loginLibertyAlliance(self, **authenticationRequestKeywords): + """Liberty Alliance Identity Provider Login Method.""" + + nextUri = X.actionUrl('loginLibertyAlliance') + for key, value in authenticationRequestKeywords.items(): + nextUri.add(key, value) + redirectPage = self.createSessionIfNeeded(nextUri) + if redirectPage is not None: + return redirectPage + + session = context.getVar('session') + session['authenticationRequestKeywords'] \ + = authenticationRequestKeywords + session['isDirty'] = 1 + authenticationRequest = sso.buildAuthenticationRequestFromKeywords( + authenticationRequestKeywords) + authenticationMethods = authenticationRequest.getAuthenticationMethods( + ) + isPassive, forceAuthentication = sso.processAuthenticationRequest( + authenticationRequest) + userId = context.getVar('userId') + if isPassive: + 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: + accountsServerRoles = { + 'password': 'passwordaccounts', + 'softwarePki': 'x509accounts', + } + accountsWeb = getWebForServerRole( + accountsServerRoles[authenticationMethods[0]]) # FIXME + return accountsWeb.login() + else: + return self.loginSucceeded2(session['authenticationMethod'], + authenticationRequest) + loginLibertyAlliance.isPublicForWeb = 1 + + def loginLocal(self, afterLoginUri = ''): + """Non Liberty Alliance local login.""" + + nextUri = X.actionUrl('loginLocal') + nextUri.add('afterLoginUri', afterLoginUri) + redirectPage = self.createSessionIfNeeded(nextUri) + if redirectPage is not None: + return redirectPage + + session = context.getVar('session') + session['afterLoginUri'] = afterLoginUri + session['isDirty'] = 1 + + authenticationMethods = ['password', 'softwarePki'] # FIXME + accountsServerRoles = { + 'password': 'passwordaccounts', + 'softwarePki': 'x509accounts', + } + accountsWeb = getWebForServerRole( + accountsServerRoles[authenticationMethods[0]]) # FIXME + return accountsWeb.login() + loginLocal.isPublicForWeb = 1 + + def loginSucceeded(self, userToken, authenticationMethod): + """Liberty Alliance Identity Provider Login Succeeded Method.""" + + session = context.getVar('session') + 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 session.has_key('afterLoginUri') and session['afterLoginUri']: + nextUri = session['afterLoginUri'] + canUseCookie = context.getVar('canUseCookie', default = 0) + if not canUseCookie: + sessionToken = context.getVar('sessionToken') + nextUri = appendToUri(nextUri, + 'sessionToken=' + sessionToken) + else: + nextUri = X.rootUrl() + return redirect(nextUri) + # Liberty Alliance login. + authenticationRequestKeywords \ + = session['authenticationRequestKeywords'] + authenticationRequest = sso.buildAuthenticationRequestFromKeywords( + authenticationRequestKeywords) + return self.loginSucceeded2(authenticationMethod, + authenticationRequest) + + def loginSucceeded2(self, authenticationMethod, authenticationRequest): + user = context.getVar('user') + serviceProviderHostName = authenticationRequest.getProviderID() + providersWeb = getWebForServerRole('providers') + serviceProviderId = providersWeb.getServiceProviderId( + serviceProviderHostName) + serviceProvider = providersWeb.getObject(serviceProviderId) + + serviceIdentification = None + if user.serviceIdentifications is not None: + for serviceIdentification in user.serviceIdentifications: + if serviceIdentification.peerHostName \ + == serviceProviderHostName: + break + 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']) + peerNameIdentifier = serviceIdentification.peerNameIdentifier + if not peerNameIdentifier: + peerNameIdentifier = serviceIdentification.localNameIdentifier + nameIdentifierPolicy = authenticationRequest.getNameIDPolicy() + assertion = sso.buildAuthenticationAssertion( + authnRequest = authenticationRequest, + issuer = context.getVar('httpHostName'), + authenticationMethod = authenticationMethod, + nameIdentifier = peerNameIdentifier, + nameIdentifierFormat = 'federated', # FIXME + idpNameIdentifier = serviceIdentification.localNameIdentifier, + idpNameIdentifierFormat = 'federated', # FIXME + ) + + profile = authenticationRequest.getProtocolProfile() + if profile == 'artifact': + assertionsProxy = getProxyForServerRole('assertions') + artifact = assertionsProxy.addAssertion(assertion.exportToString()) + url = sso.getServiceProviderAssertionArtifactHandlerUrl( + serviceProvider.assertionConsumerServiceUrl, # FIXME + artifact, + authenticationRequest.getRelayState()) + return redirect(url) + else: # profile == 'post' + authenticationResponse \ + = sso.buildAuthenticationResponseFromAuthnRequest( + providerId = context.getVar('httpHostName'), + statusCode = 'success', # FIXME + assertion = assertion, + authnRequest = authenticationRequest) + authenticationResponseEmbedded \ + = authenticationResponse.exportToEmbeddedUrl() + 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) + + buttonsBar = X.div(_class = 'buttons-bar') + form += buttonsBar + buttonsBar += X.buttonInForm('ok', 'okButton') + return writePageLayout(layout, _('Authentication Succeeded')) + finally: + context.pull(_level = 'loginSucceeded') + + def logout(self): + self.deleteUserToken() + session = context.getVar('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')) + return redirect(nextUri) + logout.isPublicForWeb = 1 + + def newPerson(self): + peopleWeb = getWebForServerRole('people') + if not peopleWeb.canAddObject(): + return accessForbidden() + + person = peopleWeb.newObject() + return self.newPersonObject(person) + + 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) + redirectPage = self.createSessionIfNeeded(nextUri) + if redirectPage is not None: + return redirectPage + + if keywords.has_key('LARES'): + # POST + authenticationResponseEmbedded = keywords['LARES'] + + # Remove base64 url encoding. + import lasso.Schemas.Schema as schema + authenticationResponseEmbeddedDecoded = schema.decodeEmbeddedUrl( + authenticationResponseEmbedded) + + authenticationResponseKeywords = {} + for segment in authenticationResponseEmbeddedDecoded.split('&'): + key, value = segment.split('=') + authenticationResponseKeywords[key] = value + + response = sso.buildAuthenticationResponseFromKeywords( + authenticationResponseKeywords) + relayState = response.getRelayState() # FIXME: Uncrypt relayState. + identityProviderHostName = response.getProviderID() + elif keywords.has_key('samlp:AssertionArtifact'): + # Artifact. + if keywords.has_key('RelayState'): + # FIXME: Uncrypt relayState. + relayState = keywords['RelayState'] + del keywords['RelayState'] + else: + relayState = None + providersWeb = getWebForServerRole('providers') + # FIXME: Don't use getRemoteIdentityProviderId(). Use instead, the + # IdentityProviderSuccintID which is encoded into the artifact. + identityProviderId = providersWeb.getRemoteIdentityProviderId() + identityProvider = providersWeb.getObject(identityProviderId) + identityProviderHostName = identityProvider.providerId + idpParsedSoapEndpoint = urlparse.urlparse( + identityProvider.soapEndpoint) + idpAddressingScheme, idpHostName, idpPath \ + = idpParsedSoapEndpoint[:3] + assertionArtifact = sso.buildAssertionArtifactFromKeywords( + keywords) + request = sso.buildRequest(assertionArtifact) + requestSoapMessage = lasso.Soap.buildMessage( + request.exportToString()) + headers = {'Content-type': 'text/xml'} + if idpAddressingScheme == 'https': + # Use HTTPS protocol. + # FIXME: Use PEM stored in glasnost attributes. + shortHostName = context.getVar('httpHostName').split('.')[0] + connection = HTTPSConnection( + idpHostName, + privateKeyFile = '/home/manou/ssl-certificates/servers/%s/%s_private_key_uncrypted.pem' % (shortHostName, shortHostName), + certificateFile = '/home/manou/ssl-certificates/servers/%s/%s_entrouvert.crt' % (shortHostName, shortHostName), + peerCaCertificateFile = '/home/manou/ssl-certificates/certification-authorities/entrouvert_and_vandoeuvre-les-nancy_ca.crt') + else: + # Use HTTP protocol. + connection = httplib.HTTPConnection(idpHostName) + connection.request( + 'POST', + idpPath, + requestSoapMessage, + headers) + responseSoapMessageFile = connection.getresponse() + responseSoapMessage = responseSoapMessageFile.read() + try: + responseXml = lasso.Soap.extractFromMessage( + responseSoapMessage) + except: # FIXME + raise responseSoapMessage + responseDom = parseString(responseXml) + response = importFromNode(responseDom, 1) + else: + # FIXME: Is this needed? Can idp directly return a status? + response = buildStatusFromKeywords(keywords) + statusCode = response.getStatusCode() + 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) + + assertion = response.getAssertion() + idpNameIdentifier = assertion.getIDPProvidedNameIdentifier() + nameIdentifier = assertion.getNameIdentifier() + if nameIdentifier and nameIdentifier != idpNameIdentifier: + try: + userToken = self.checkIdentityLocalNameIdentifier( + identityProviderHostName, nameIdentifier) + except faults.WrongNameIdentifier: + userToken = None + else: + try: + userToken = self.checkIdentityPeerNameIdentifier( + identityProviderHostName, idpNameIdentifier) + except faults.WrongNameIdentifier: + userToken = None + 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() + session = context.getVar('session') + session['authenticationMethod'] = assertion.getAuthenticationMethod() + session['isDirty'] = 1 + if userToken is None: + 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.checkIdentityLocalNameIdentifier( + identityProviderHostName, nameIdentifier) + else: + userToken = self.checkIdentityPeerNameIdentifier( + 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') + 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) + + return redirect(nextUri) + processAuthenticationResponse.isPublicForWeb = 1 + + def processSoapRequest(self): + """Liberty Alliance Identity Provider SOAP Request Processing Method. + + """ + + requestSoapMessage = context.getVar('xmlPostRaw') # FIXME: Use xmlPost. + requestXml = lasso.Soap.extractFromMessage(requestSoapMessage) + requestDom = parseString(requestXml) + request = importFromNode(requestDom, 1) + 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 = parseString(assertionXml) + assertion = importFromNode(assertionDom, 1) + response = sso.buildResponse('success', assertion) + responseSoapMessage = lasso.Soap.buildMessage( + response.exportToString()) + req = context.getVar('req') + req.content_type = 'text/xml' + req.send_http_header() + req.write(responseSoapMessage) + return OK + processSoapRequest.isPublicForWeb = 1 diff --git a/shared/web/ObjectsWeb.py b/shared/web/ObjectsWeb.py index eaa10f18..f4da3d14 100644 --- a/shared/web/ObjectsWeb.py +++ b/shared/web/ObjectsWeb.py @@ -904,7 +904,6 @@ class ObjectsWebMixin(AdministrableWebMixin): if context.getVar('debug'): raise return accessForbidden() # TODO: return failure ? - if result: return result diff --git a/shared/web/PasswordAccountsWeb.py b/shared/web/PasswordAccountsWeb.py new file mode 100644 index 00000000..b7c141ff --- /dev/null +++ b/shared/web/PasswordAccountsWeb.py @@ -0,0 +1,191 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Password Accounts Web""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.common.context as context +import glasnost.common.faults as faults +import glasnost.common.ObjectsCommon as commonObjects +import glasnost.common.tools_new as commonTools +import glasnost.common.xhtmlgenerator as X + +import glasnost.proxy.PasswordAccountsProxy as proxyPasswordAccounts + +import ObjectsWeb as objects +from tools import accessForbidden, getWebForServerRole, writePageLayout + + +class AdminPasswordAccounts(objects.AdminMixin, + proxyPasswordAccounts.AdminPasswordAccounts): + pass +objects.register(AdminPasswordAccounts) + + +class Login(objects.ObjectWebMixin, commonObjects.ObjectCommon): + id_kindName = None + + language_kindName = None + + login = None + class login_kindClass: + _kindName = 'String' + balloonHelp = N_('Enter the username you use on this site.') + isRequired = 1 + isTranslatable = 0 + label = N_('Username') + textMaxLength = 40 + widget_size = 15 + + password = None + class password_kindClass: + _kindName = 'Password' + balloonHelp = N_('Enter your secret password.') + isRequired = 1 + label = N_('Password') + textMaxLength = 15 + widget_size = 15 + + +class PasswordAccount(objects.ObjectWebMixin, + proxyPasswordAccounts.PasswordAccount): +## skipPassword = 1 + +## def getEditLayoutSlotNames(self, fields, parentSlot = None): +## slotNames = objects.ObjectWebMixin.getEditLayoutSlotNames(self, +## fields, parentSlot = parentSlot) +## if self.skipPassword: +## slotNames.remove('password') +## return slotNames + pass +objects.register(PasswordAccount) + + +class PasswordAccountsWeb(objects.ObjectsWebMixin, + proxyPasswordAccounts.PasswordAccountsProxy): + def login(self): + object = Login() + return self.loginObject(object) + login.isPublicForWeb = 1 + + def loginObject(self, object): + req = context.getVar('req') + req.headers_out['Cache-Control'] = 'no-cache, must-revalidate' + +## object.skipPassword = 0 + context.push(_level = 'loginObject', layoutMode = 'edit') + try: + layout = X.array() + + if context.getVar('error'): + layout += object.getErrorLayout() + + # The instruction submitUrl = X.actionUrl('loginSubmit') + # doesn't work because the login method can be called from + # IdentitiesWeb. + submitUrl = X.roleUrl(self.serverRole, action = 'loginSubmit') + if context.getVar('virtualHost').useHTTPS: + hostNameAndPort = commonTools.makeHttpHostNameAndPort( + context.getVar('httpHostName'), + context.getVar('httpPort')) + submitUrl = 'https://%s%s' % (hostNameAndPort, submitUrl) + + form = X.form(action = submitUrl, enctype = 'multipart/form-data', + method = 'post') + layout += form + + if context.getVar('nextUri'): + form += X.div(X.input(name = 'nextUri', type = 'hidden', + value = context.getVar('nextUri'))) + + form += object.getEditLayout(fields = None) + + buttonsBar = X.div(_class = 'buttons-bar') + form += buttonsBar + buttonsBar += X.buttonInForm('login', 'loginButton') +## if 1: # TODO: check if emailPassword is available +## buttonsBar += X.buttonInForm( +## 'send-password-by-email', 'sendButton') + return writePageLayout(layout, _('Login')) + finally: + context.pull(_level = 'loginObject') + + def loginSubmit(self, **keywords): + if keywords is None: + keywords = {} +## sendPasswordByEmail = isButtonSelected('sendButton', keywords) + + object = Login() + object.submitFields(keywords) + + if context.getVar('again'): + return self.loginObject(object) + +## if sendPasswordByEmail: +## try: +## self.emailPassword(object) +## except: +## return failure(_('An error occured while sending the password.'), +## X.rootUrl()) +## return success(_('The password has been sent successfully.'), X.rootUrl()) + + try: + userToken = self.checkObjectAuthentication( + object.login, object.password) + except faults.WrongLogin, fault: + context.getVar('error', 1) + object.setError('self.login', fault) + return self.loginObject(object) + except faults.WrongPassword, fault: + context.getVar('error', 1) + object.setError('self.password', fault) + return self.loginObject(object) + except: + if context.getVar('debug'): + raise + return accessForbidden() + identitiesWeb = getWebForServerRole('identities') + return identitiesWeb.loginSucceeded(userToken, 'password') + loginSubmit.isPublicForWeb = 1 + diff --git a/shared/web/PeopleWeb.py b/shared/web/PeopleWeb.py index 0c3b3474..6ca93a58 100644 --- a/shared/web/PeopleWeb.py +++ b/shared/web/PeopleWeb.py @@ -92,9 +92,6 @@ class Person(ObjectWebMixin, Person): nickname_kind_widget_size = 40 nickname_kind_widgetName = 'InputText' - voteTokens_kind_stateInEditMode = 'hidden' - voteTokens_kind_stateInViewMode = 'hidden' - def getEditLayout(self, fields, parentSlot = None): if context.getVar('userId') != self.id: return ObjectWebMixin.getEditLayout(self, fields, parentSlot) @@ -147,7 +144,6 @@ register(Person) class PeopleWeb(ObjectsWebMixin, PeopleProxy): - def getObject_handleResult(self, lazyObject): object = PeopleProxy.getObject_handleResult(self, lazyObject) groupsProxy = getProxyForServerRole('groups') @@ -165,65 +161,6 @@ class PeopleWeb(ObjectsWebMixin, PeopleProxy): cmp(labels[id1], labels[id2])) return ids - def getViewAboveButtonsBarLayout(self, object, fields): - ballotsWeb = getWebForServerRole('ballots') - electionsWeb = getWebForServerRole('elections') - layout = X.array() - layout += 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', default = '') - knownRoles = context.getVar('knownRoles') - if userId and 'ballots' in knownRoles and 'elections' in knownRoles: - # Only an identified user can see someone's votes. - ballotsWeb = getWebForServerRole('ballots') - electionsWeb = getWebForServerRole('elections') - 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 submitAddObject(self, object): error = 0 try: @@ -282,8 +219,8 @@ class PeopleWeb(ObjectsWebMixin, PeopleProxy): layout = X.array() layout += X.asIs(_("""

Note that user accounts should now be created from the -authentication page. -

""") % X.roleUrl('authentication')) +identities page. +

""") % X.roleUrl('identities')) ids = self.getSortedIds(partialObjects) layout += self.getObjectsLayout(partialObjects, ids, []) layout += self.getViewAllButtonsBarLayout() diff --git a/shared/web/PreferencesWeb.py b/shared/web/PreferencesWeb.py index d63c79be..3581c2ee 100644 --- a/shared/web/PreferencesWeb.py +++ b/shared/web/PreferencesWeb.py @@ -79,9 +79,6 @@ class Preference(ObjectWebMixin, Preference): currency_kind_widget_labels = accounting.currencyLabels currency_kind_widgetName = 'Select' - objectsMemory_kind_stateInEditMode = 'hidden' - objectsMemory_kind_stateInViewMode = 'hidden' - spellcheckEntries_kind_defaultValue = 1 spellcheckEntries_kind_widget_fieldLabel = N_('Spellcheck Entries') spellcheckEntries_kind_widget_labels = { diff --git a/shared/web/ProvidersWeb.py b/shared/web/ProvidersWeb.py new file mode 100644 index 00000000..a70277ed --- /dev/null +++ b/shared/web/ProvidersWeb.py @@ -0,0 +1,109 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 Providers Web""" + +__version__ = '$Revision$'[11:-2] + + +import glasnost.common.tools_new as commonTools + +import glasnost.proxy.ProvidersProxy as proxyProviders + +import ObjectsWeb as objects + + +class AdminProviders(objects.AdminMixin, proxyProviders.AdminProviders): + pass +objects.register(AdminProviders) + + +class ProviderMixin(objects.ObjectWebMixin): + className = None + class className_kindClass: + _kindName = 'Choice' + defaultValue = 'IdentityProvider' + importExport = 'private' + isRequired = 1 + isRequiredInEditMode = 1 + values = [ + 'IdentityProvider', + 'ServiceProvider', + ] + widget_apply = 1 + widget_fieldLabel = N_('Type') + widget_labels = { + 'IdentityProvider': N_('Identity Provider'), + 'ServiceProvider': N_('Service Provider'), + } + + +class IdentityProvider(ProviderMixin, proxyProviders.IdentityProvider): + pass +objects.register(IdentityProvider) + + +class ServiceProvider(ProviderMixin, proxyProviders.ServiceProvider): + pass +objects.register(ServiceProvider) + + +class ProvidersWeb(objects.ObjectsWebMixin, proxyProviders.ProvidersProxy): + def getObject_handleResult(self, lazyObject): + object = proxyProviders.ProvidersProxy.getObject_handleResult( + self, lazyObject) + object.className = object.__class__.__name__ + return object + + def newObject(self, fields = None): + provider = IdentityProvider() # Fake provider to get className. + if not fields: + provider.className = 'IdentityProvider' + return provider + classNameSlot = provider.getSlot('className') + className = classNameSlot.getWidget().submit(classNameSlot, fields) + if not className: + className = 'IdentityProvider' + provider = commonTools.newThing('object', 'providers.%s' % className) + provider.className = className + return provider + diff --git a/shared/web/VotesWeb.py b/shared/web/VotesWeb.py index 0f3bd695..b7d58046 100644 --- a/shared/web/VotesWeb.py +++ b/shared/web/VotesWeb.py @@ -369,7 +369,7 @@ class VoteMixin(ObjectWebMixin): voterId_kind_hasToSubmitField = 0 voterId_kind_stateInEditMode = 'read-only' voterId_kindName = 'Id' - voterId_kind_serverRoles = ['people'] + voterId_kind_serverRoles = ['identities'] voterId_kind_widget_fieldLabel = N_('Voter') voterId_kind_widgetName = 'SelectId' @@ -694,7 +694,7 @@ class VotesWeb(ObjectsWebMixin, VotesProxy): def edit(self, id = '', electionId = ''): ballotsWeb = getWebForServerRole('ballots') electionsWeb = getWebForServerRole('elections') - peopleWeb = getWebForServerRole('people') + identitiesWeb = getWebForServerRole('identities') userId = context.getVar('userId', default = '') userToken = context.getVar('userToken', default = '') if not userToken: @@ -721,7 +721,7 @@ class VotesWeb(ObjectsWebMixin, VotesProxy): return accessForbidden() election = electionsWeb.getObject(electionId) rememberObject(electionId) - voter = peopleWeb.getObject(userId) + voter = identitiesWeb.getObject(userId) if voter.voteTokens is not None \ and voter.voteTokens.has_key(electionId): voteToken = voter.voteTokens[electionId] @@ -826,7 +826,7 @@ class VotesWeb(ObjectsWebMixin, VotesProxy): def view(self, id = '', electionId = '', voterId = ''): ballotsWeb = getWebForServerRole('ballots') electionsWeb = getWebForServerRole('elections') - peopleWeb = getWebForServerRole('people') + identitiesWeb = getWebForServerRole('identities') userId = context.getVar('userId', default = '') if id: vote = ballotsWeb.getVote(id) @@ -851,15 +851,15 @@ class VotesWeb(ObjectsWebMixin, VotesProxy): or not electionsWeb.hasObject(electionId): return pageNotFound() if not voterId \ - or not peopleWeb.hasObject(voterId): + or not identitiesWeb.hasObject(voterId): return pageNotFound() if not electionsWeb.canGetObject(electionId): return accessForbidden() - if not peopleWeb.canGetObject(voterId): + if not identitiesWeb.canGetObject(voterId): return accessForbidden() election = electionsWeb.getObject(electionId) rememberObject(electionId) - voter = peopleWeb.getObject(voterId) + voter = identitiesWeb.getObject(voterId) if voter.voteTokens is not None \ and voter.voteTokens.has_key(electionId): voteToken = voter.voteTokens[electionId] diff --git a/shared/web/X509AccountsWeb.py b/shared/web/X509AccountsWeb.py new file mode 100644 index 00000000..37485362 --- /dev/null +++ b/shared/web/X509AccountsWeb.py @@ -0,0 +1,125 @@ +# -*- coding: iso-8859-15 -*- + + +# Glasnost +# By: Odile Bénassy +# Romain Chantereau +# Nicolas Clapiès +# Pierre-Antoine Dejace +# Thierry Dulieu +# Florent Monnier +# Cédric Musso +# Frédéric Péters +# Benjamin Poussin +# Emmanuel Raviart +# Sébastien Régnier +# Emmanuel Saracco +# +# 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 X509 Accounts Web""" + +__version__ = '$Revision$'[11:-2] + + +import os +from mod_python import apache + +import glasnost.common.context as context +import glasnost.common.faults as faults +import glasnost.common.ObjectsCommon as commonObjects +import glasnost.common.tools_new as commonTools +import glasnost.common.xhtmlgenerator as X + +import glasnost.proxy.X509AccountsProxy as proxyX509Accounts + +import ObjectsWeb as objects +from tools import accessForbidden, getWebForServerRole, writePageLayout + + +class AdminX509Accounts(objects.AdminMixin, + proxyX509Accounts.AdminX509Accounts): + pass +objects.register(AdminX509Accounts) + + +class X509Account(objects.ObjectWebMixin, + proxyX509Accounts.X509Account): + pass +objects.register(X509Account) + + +class X509AccountsWeb(objects.ObjectsWebMixin, + proxyX509Accounts.X509AccountsProxy): + def returnToRetryPage(self, stringError): + req = context.getVar('req') + req.headers_out['Cache-Control'] = 'no-cache, must-revalidate' + + context.push(_level = 'returnToRetryPage', layoutMode = 'edit') + try: + context.push(_level = 'login', layoutMode = 'edit') + + layout = X.array() + submitUrl = X.roleUrl(self.serverRole, action = 'login') + form = X.form(action = submitUrl, + enctype = 'multipart/form-data', + method = 'post') + layout += form + buttonsBar = X.div(_class = 'buttons-bar') + form += buttonsBar + buttonsBar += X.buttonInForm('retry', 'retryButton') + return writePageLayout(layout, _(stringError)) + finally: + context.pull(_level = 'returnToRetryPage') + + def login(self): + serial = None + + req = context.getVar('req') + env = apache.build_cgi_env(req) + + try: + sslClientVerify = env['SSL_CLIENT_VERIFY'] + except KeyError: + return self.returnToRetryPage(_('SSL not activated')) + + if sslClientVerify != 'SUCCESS': + return self.returnToRetryPage(_( + 'Client certificate verification error')) + else: + serial = env['SSL_CLIENT_M_SERIAL'] + try: + userToken = self.checkObjectAuthentication(serial) + except faults.WrongX509Serial: + return self.returnToRetryPage(_( + 'Unknown certificate serial number = %s' % serial)) + except: + if context.getVar('debug'): + raise + return accessForbidden() + identitiesWeb = getWebForServerRole('identities') + return identitiesWeb.loginSucceeded(userToken, 'softwarePki') + login.isPublicForWeb = 1 diff --git a/shared/web/tools.py b/shared/web/tools.py index 659d96d0..1817b37c 100644 --- a/shared/web/tools.py +++ b/shared/web/tools.py @@ -151,8 +151,8 @@ def accessForbidden(dontAskForLogin = 0): cleanedUpUri = cleanUpUnparsedUri([], 'http') cleanedUpUri = cleanedUpUri.replace('/people/submit', '/').replace( '/index.py', '/') - loginUrl = X.roleUrl('login').add('nextUri', cleanedUpUri).add( - 'access', 'forbidden') + loginUrl = X.roleUrl('identities', action = 'login').add( + 'nextUri', cleanedUpUri).add('access', 'forbidden') hostNameAndPort = commonTools.makeHttpHostNameAndPort( context.getVar('httpHostName'), context.getVar('httpPort')) @@ -364,10 +364,8 @@ def redirectPermanently(url): def rememberObject(id): - preferencesWeb = getWebForServerRole('preferences') - if not preferencesWeb: - return - preferencesWeb.rememberId(id) + identitiesWeb = getWebForServerRole('identities') + identitiesWeb.rememberId(id) def repairMimeType(fieldValue, fileName): @@ -582,7 +580,8 @@ def getTemplateVars(): cleanedUpUri = cleanUpUnparsedUri([]) cleanedUpUri = cleanedUpUri.replace('/people/submit', '/').replace( '/index.py', '/') - loginUrl = X.roleUrl('login').add('nextUri', cleanedUpUri) + loginUrl = X.roleUrl('identities', action = 'login').add( + 'nextUri', cleanedUpUri) hostNameAndPort = commonTools.makeHttpHostNameAndPort( context.getVar('httpHostName'), context.getVar('httpPort')) @@ -592,7 +591,7 @@ def getTemplateVars(): 'nameAndQuery': loginUrl, } - logoutUrl = X.roleUrl('authentication', 'logout') + logoutUrl = X.roleUrl('identities', action = 'logout') httpScriptDirectoryPath = context.getVar('httpScriptDirectoryPath') @@ -600,15 +599,15 @@ def getTemplateVars(): aboutUrl = X.roleUrl('about').getAsUrl() logoutButton = X.buttonStandalone('logout', logoutUrl).getAsXml() - - prefsButton = X.buttonStandalone('prefs', X.roleUrl('preferences') - ).getAsXml() - prefsUrl = X.roleUrl('preferences').getAsUrl() + + if userId: + prefsButton = X.buttonStandalone('prefs', X.idUrl(userId)).getAsXml() + prefsUrl = X.idUrl(userId).getAsUrl() loginButton = X.buttonStandalone('login', loginUrl).getAsXml() - # FIXME: should take the favourite authentication method - newAccountUrl = X.roleUrl('authentication-login-password', 'newAccount') + # FIXME: should take the favourite identities method + newAccountUrl = X.roleUrl('identities', action = 'newAccount') newAccountButton = X.buttonStandalone( 'new-account', newAccountUrl).getAsXml() newAccountUrl = newAccountUrl.getAsUrl() diff --git a/shared/web/widgets.py b/shared/web/widgets.py index b94a4a98..8851db12 100644 --- a/shared/web/widgets.py +++ b/shared/web/widgets.py @@ -1514,8 +1514,9 @@ class SelectId(WidgetMixin, proxyWidgets.SelectId): layout += X.menuIds( serverRoles, attributes = selectAttributes, - fieldValue = fieldValue, fullRoles = showFullList, menus = menus, - noneLabel = self.noneLabel, permanentIds = kind.permanentIds) + fieldValue = fieldValue, fullRoles = showFullList, + menus = menus, noneLabel = self.noneLabel, + permanentIds = kind.permanentIds) if showOthersButton: roles = [] for role in serverRoles: @@ -1690,12 +1691,12 @@ class TextArea(WidgetMixin, proxyWidgets.TextArea): formattedText = parsers.makeHtmlFromUnformattedText( fieldValue) formattedText = replaceSpecialTags(formattedText) - preferences = context.getVar('preferences') - if (not preferences or preferences.spellcheckEntries) and \ - slot.getObject().hasSlotName('language'): + user = context.getVar('user') + if user is not None and user.spellcheckEntries \ + and slot.getObject().hasSlotName('language'): languageSlot = slot.getObject().getSlot('language') formattedText = spellcheck(formattedText, - languageSlot.getValue()) + languageSlot.getValue()) if formattedText: layout += X.div(_class = 'preview')(X.asIs(formattedText)) diff --git a/system/groups.py b/system/groups.py index ebfdb7f7..30726f5a 100755 --- a/system/groups.py +++ b/system/groups.py @@ -69,7 +69,7 @@ class BuildCase01_generalPublicGroup(unittest.TestCase): """Build the general public group.""" group = groups.GroupAll() - group.acceptedRoles = ['people', 'ldappeople'] + group.acceptedRoles = ['identities'] group.language = 'en' group.name = N_('General Public') ## group.itemIds = [system.generalPublicId] @@ -82,7 +82,7 @@ class BuildCase01_generalPublicGroup(unittest.TestCase): """Build the logged users group.""" group = groups.GroupRole() - group.acceptedRoles = [ 'people', 'ldappeople' ] + group.acceptedRoles = ['identities'] group.language = 'en' group.name = N_('Logged Users') groupId = groupsProxy.addObject(group) diff --git a/templates/default/forms.tal b/templates/default/forms.tal index 97e6c358..8495f7a0 100644 --- a/templates/default/forms.tal +++ b/templates/default/forms.tal @@ -1,5 +1,4 @@ -