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

447 lines
18 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
# Romain Chantereau <rchantereau@entrouvert.com>
# Nicolas Clapiès <nclapies@easter-eggs.org>
# Pierre-Antoine Dejace <padejace@entrouvert.be>
# Thierry Dulieu <tdulieu@easter-eggs.com>
# Florent Monnier <monnier@codelutin.com>
# Cédric Musso <cmusso@easter-eggs.org>
# Frédéric Péters <fpeters@entrouvert.be>
# Benjamin Poussin <poussin@codelutin.com>
# Emmanuel Raviart <eraviart@entrouvert.com>
# Sébastien Régnier <regnier@codelutin.com>
# Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
# Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
# Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
__doc__ = """Glasnost Password Accounts Server"""
__version__ = '$Revision$'[11:-2]
import copy
import sha
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, sendMail
import glasnost.common.tools_new as commonTools
import glasnost.server.ObjectsServer as objects
from glasnost.proxy.GroupsProxy import getSetContainedIds
from glasnost.proxy.tools import getObject, getProxy, getProxyForServerRole
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)
def modify(self, changes, givenSlotNames = None):
inClearText = self.storePasswordsInClearText
objects.AdminServerMixin.modify(self, changes,
givenSlotNames = givenSlotNames)
if not inClearText and not self.storePasswordsInClearText:
# we now have to hash every passwords
server = self.getServer()
virtualServerId = context.getVar('applicationId')
virtualServer = server.getVirtualServer(virtualServerId)
for object in virtualServer.objects.values():
object.password = sha.new(object.password).hexdigest()
virtualServer.markAllAsDirtyFIXME()
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):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getServer().getVirtualServer(virtualServerId)
if virtualServer.objectsByLogin.has_key(self.login):
del virtualServer.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
if not virtualServer.admin.storePasswordsInClearText:
self.password = sha.new(self.password).hexdigest()
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
if not virtualServer.admin.userCanChoosePassword or \
not object.password:
object.password = commonTools.makepassword()
self.emailPasswordToUser(object, notYetHashed = 1)
if not virtualServer.admin.storePasswordsInClearText:
object.password = sha.new(object.password).hexdigest()
virtualServer.markCoreAsDirty()
return objectId
def canChangePassword(self):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
return virtualServer.admin.userCanChoosePassword
def canModifyObject(self, objectId):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
if not virtualServer.canLoadObjectCore(objectId):
return 0
object = virtualServer.loadObjectCore(objectId)
if object.identityId == getProxyForServerRole('identities').getUserId():
return 1
return objects.ObjectsServer.canModifyObject(self, objectId)
def changePassword(self, oldPassword, newPassword):
userId = getProxyForServerRole('identities').getUserId()
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
if not virtualServer.admin.storePasswordsInClearText:
cmpPassword = sha.new(oldPassword).hexdigest()
else:
cmpPassword = oldPassword
for object in virtualServer.objects.values():
if object.identityId == userId:
if object.password != cmpPassword:
raise faults.WrongPassword(oldPassword)
object.password = newPassword
if not virtualServer.admin.storePasswordsInClearText:
object.password = sha.new(object.password).hexdigest()
virtualServer.markObjectAsDirty(object)
return
# TODO: hum, should be faults.WrongIdentity
raise faults.WrongLogin('')
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 not virtualServer.admin.storePasswordsInClearText:
cmpPassword = sha.new(password).hexdigest()
else:
cmpPassword = password
if object.password and cmpPassword != object.password:
raise faults.WrongPassword(password)
identitiesProxy = getProxy(object.identityId)
return [identitiesProxy.getUserToken(object.identityId), 'password']
def emailPassword(self, loginImport):
login = iso8859_15(loginImport)
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
for object in virtualServer.objects.values():
if object.login == login:
self.emailPasswordToUser(object)
def emailPasswordToUser(self, object, notYetHashed = 0):
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
if not notYetHashed and \
not virtualServer.admin.storePasswordsInClearText:
# TODO: mail user explaining we can generate a new password for
# him and provide him with an url with a token (so that others
# can't force password changes)
print 'Not sending because passwords are not in plain text'
return
identitiesProxy = getProxy(object.identityId)
#identity = identitiesProxy.getObject(object.identityId)
#if not identity.personId:
# print 'no personId for this identity'
# return
emailAddress = identitiesProxy.getObjectEmail(object.identityId)
if not emailAddress:
raise faults.BadEmailAddress('')
personName, personLanguage = identitiesProxy.getObjectLabelAndLanguage(
object.identityId)
toAddress = [emailAddress]
password = object.password
messageFileName = commonTools.getConfig(
commonTools.extractDispatcherId(object.id),
'WelcomeEmail-%s' % personLanguage)
if not messageFileName:
messageFileName = commonTools.getConfig(
commonTools.extractDispatcherId(object.id), 'WelcomeEmail')
messageSubject = commonTools.getConfig(
commonTools.extractDispatcherId(object.id),
'WelcomeEmailSubject',
default = '[Glasnost] login & password')
message = """\
Here are the Glasnost login & password you have requested.
Web Site: %(hostName)s
User: %(user)s
Login: %(login)s
Password: %(password)s
The Glasnost administrator - %(fromAddress)s
"""
if messageFileName:
message = open(messageFileName).read()
virtualServerId = context.getVar('applicationId')
hostName = getProxyForServerRole('virtualhosts').getHostName(
virtualServerId)
fromAddresses = self.getAdminEmailAddresses(stopAsap = 1)
message = message % {
'user': personName,
'fromAddress': fromAddresses[0],
'hostName': hostName,
'login': object.login,
'password': object.password,
}
sendMail(
mailFrom = fromAddresses[0],
mailTo = toAddress,
mailSubject = messageSubject,
mailMessage = message,
moreHeaders = {
'Content-Type': 'text/plain; charset=iso-8859-1',
'Content-Transfer-Encoding': '8bit',
}
)
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, 'adminsSet'):
admin.adminsSet = authenticationAdmin.adminsSet
if hasattr(authenticationAdmin, 'writersSet'):
admin.writersSet = authenticationAdmin.writersSet
if hasattr(authenticationAdmin, 'readersSet'):
admin.readersSet = authenticationAdmin.readersSet
if hasattr(authenticationAdmin, 'stockPasswordsInClearText'):
admin.storePasswordsInClearText \
= authenticationAdmin.stockPasswordsInClearText
if hasattr(authenticationAdmin, 'userCanChoosePassword'):
admin.userCanChoosePassword \
= authenticationAdmin.userCanChoosePassword
virtualServer.markAdminAsDirty(virtualServer.admin)
virtualServer.nextLocalId = 1
virtualServer.markCoreAsDirty()
personIds = authenticationVirtualServer.authentications.keys()
personIds.sort(
lambda x,y: cmp(
int(commonTools.extractLocalId(x)),
int(commonTools.extractLocalId(y)) ))
if personIds:
virtualServer.nextLocalId = int(commonTools.extractLocalId(
personIds[-1])) + 1
for personId in personIds:
authentication = authenticationVirtualServer.authentications[
personId]
passwordAccount = PasswordAccount()
passwordAccount.login = authentication.login
passwordAccount.password = authentication.password
passwordAccount.id = '%s/%s' % (
commonTools.makeApplicationId(personId,
'passwordaccounts'),
commonTools.extractLocalId(personId))
passwordAccount.identityId = '%s/%s' % (
commonTools.makeApplicationId(personId, 'identities'),
commonTools.extractLocalId(personId))
virtualServer.objects[passwordAccount.id] = passwordAccount
virtualServer.objectsByLogin[
passwordAccount.login] = passwordAccount
passwordAccount.saveNonCore()
passwordAccount.releaseNonCore()
virtualServer.markObjectAsDirty(passwordAccount)
virtualServer.markCoreAsDirty()
def getAdminEmailAddresses(self, stopAsap = 0):
# if an address is set in the config file we use it; otherwise we
# must grab addresses from self.admin.adminsSet
email = commonTools.getConfig(
commonTools.extractDispatcherId(context.getVar('dispatcherId')),
'AdminEmailAddress')
if email:
return [email]
virtualServerId = context.getVar('applicationId')
virtualServer = self.getVirtualServer(virtualServerId)
try:
adminIds = getSetContainedIds(
virtualServer.admin.adminsSet, ['identities'],
raiseWhenUncountable = 1)
except faults.UncountableGroup:
adminIds = []
identitiesProxy = getProxyForServerRole('identities')
toAddresses = []
for adminId in adminIds:
emailAddress = identitiesProxy.getObjectEmail(adminId)
if emailAddress:
toAddresses.append(emailAddress)
if stopAsap:
return toAddresses
if not toAddresses:
return [virtualServer.adminEmailAddress]
return toAddresses
def registerPublicMethods(self):
objects.ObjectsServer.registerPublicMethods(self)
self.registerPublicMethod('canChangePassword')
self.registerPublicMethod('changePassword')
self.registerPublicMethod('checkObjectAuthentication',
self.checkObjectAuthenticationXmlRpc)
self.registerPublicMethod('emailPassword')
passwordAccountsServer = PasswordAccountsServer()
if __name__ == "__main__":
passwordAccountsServer.launch(applicationName, applicationRole)