From a6962d4a5a0c5e59b07616ee27bc140d1f099aa2 Mon Sep 17 00:00:00 2001 From: eraviart <> Date: Mon, 22 Dec 2003 15:55:46 +0000 Subject: [PATCH] Fusion de la branche glasnost-lasso avec le tronc. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upgrade en reprenant les informations des anciens serveurs d'authentification. Cela devrait permettre de se logguer (un peu testé), mais cela ne marche pas encore pour les anciens votes par exemple. Amélioration du mécanisme d'upgrade : les fonctions d'upgrade peuvent être maintenant aussi mises ailleurs que dans les serveurs. N'enregistre plus les données salies lorsqu'un serveur s'appelle directement (afin d'éviter une boucle sans fin). --- config.in | 26 +- glasnost-web/code/webhandler.py | 70 +- glasnost-web/index.py | 6 +- glasnost-web/login.py | 20 - glasnost-web/sessions.py | 56 +- profiles/basic.xml.in | 5 +- .../AppointmentsServer/AppointmentsServer.py | 102 +-- servers/ArticlesServer/ArticlesServer.py | 18 +- servers/AssertionsServer/AssertionsServer.py | 142 ++++ .../AuthenticationLoginPasswordServer.py | 2 +- .../AuthenticationServer.py | 3 +- servers/BallotsServer/BallotsServer.py | 36 +- servers/CommentsServer/CommentsServer.py | 2 +- servers/ElectionsServer/ElectionsServer.py | 22 +- servers/GroupsServer/GroupsServer.py | 8 +- servers/IdentitiesServer/IdentitiesServer.py | 411 ++++++++++ .../PasswordAccountsServer.py | 232 ++++++ servers/PeopleServer/PeopleServer.py | 153 +--- .../PreferencesServer/PreferencesServer.py | 50 +- servers/ProvidersServer/ProvidersServer.py | 116 +++ servers/SessionsServer/SessionsServer.py | 8 +- .../TranslationsServer/TranslationsServer.py | 16 +- .../UploadFilesServer/UploadFilesServer.py | 6 +- .../VirtualHostsServer/VirtualHostsServer.py | 4 +- .../X509AccountsServer/X509AccountsServer.py | 161 ++++ shared/common/ArticlesCommon.py | 2 +- shared/common/CardsCommon.py | 32 +- shared/common/ElectionsCommon.py | 2 +- shared/common/GradesCommon.py | 2 +- shared/common/GroupsCommon.py | 8 +- shared/common/IdentitiesCommon.py | 188 +++++ shared/common/ObjectsCommon.py | 2 +- shared/common/PasswordAccountsCommon.py | 136 ++++ shared/common/PeopleCommon.py | 37 +- shared/common/PreferencesCommon.py | 6 +- shared/common/ProvidersCommon.py | 180 +++++ shared/common/VirtualHostsCommon.py | 2 + shared/common/VotesCommon.py | 2 +- shared/common/X509AccountsCommon.py | 99 +++ shared/common/XhtmlGenerator.py | 12 +- shared/common/faults.py | 35 +- shared/common/kinds.py | 95 ++- shared/common/slots.py | 4 +- shared/common/things.py | 32 + shared/common/tools.py | 16 +- shared/common/xhtmlgenerator.py | 6 +- shared/gtk/IdentitiesGtk.py | 66 ++ shared/gtk/PasswordAccountsGtk.py | 62 ++ shared/gtk/ProvidersGtk.py | 67 ++ shared/proxy/AssertionsProxy.py | 72 ++ shared/proxy/IdentitiesProxy.py | 169 ++++ shared/proxy/PasswordAccountsProxy.py | 86 +++ shared/proxy/PeopleProxy.py | 172 +---- shared/proxy/PreferencesProxy.py | 8 - shared/proxy/ProvidersProxy.py | 88 +++ shared/proxy/SessionsProxy.py | 16 +- shared/proxy/X509AccountsProxy.py | 75 ++ shared/server/ObjectsServer.py | 161 ++-- shared/server/xmlrpcServer.py | 2 +- shared/web/AuthenticationLoginPasswordWeb.py | 4 +- shared/web/CardsWeb.py | 8 +- shared/web/ElectionsWeb.py | 15 +- shared/web/IdentitiesWeb.py | 721 ++++++++++++++++++ shared/web/ObjectsWeb.py | 1 - shared/web/PasswordAccountsWeb.py | 191 +++++ shared/web/PeopleWeb.py | 67 +- shared/web/PreferencesWeb.py | 3 - shared/web/ProvidersWeb.py | 109 +++ shared/web/VotesWeb.py | 14 +- shared/web/X509AccountsWeb.py | 125 +++ shared/web/tools.py | 27 +- shared/web/widgets.py | 13 +- system/groups.py | 4 +- templates/default/forms.tal | 3 +- tests/GroupsTests.py | 2 +- 75 files changed, 4107 insertions(+), 817 deletions(-) create mode 100755 servers/AssertionsServer/AssertionsServer.py create mode 100755 servers/IdentitiesServer/IdentitiesServer.py create mode 100755 servers/PasswordAccountsServer/PasswordAccountsServer.py create mode 100755 servers/ProvidersServer/ProvidersServer.py create mode 100755 servers/X509AccountsServer/X509AccountsServer.py create mode 100644 shared/common/IdentitiesCommon.py create mode 100644 shared/common/PasswordAccountsCommon.py create mode 100644 shared/common/ProvidersCommon.py create mode 100644 shared/common/X509AccountsCommon.py create mode 100644 shared/gtk/IdentitiesGtk.py create mode 100644 shared/gtk/PasswordAccountsGtk.py create mode 100644 shared/gtk/ProvidersGtk.py create mode 100644 shared/proxy/AssertionsProxy.py create mode 100644 shared/proxy/IdentitiesProxy.py create mode 100644 shared/proxy/PasswordAccountsProxy.py create mode 100644 shared/proxy/ProvidersProxy.py create mode 100644 shared/proxy/X509AccountsProxy.py create mode 100644 shared/web/IdentitiesWeb.py create mode 100644 shared/web/PasswordAccountsWeb.py create mode 100644 shared/web/ProvidersWeb.py create mode 100644 shared/web/X509AccountsWeb.py 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 @@ -