diff --git a/addons/base_crypt/__terp__.py b/addons/base_crypt/__openerp__.py similarity index 93% rename from addons/base_crypt/__terp__.py rename to addons/base_crypt/__openerp__.py index 400a277ca81..fcc5fa3c60d 100755 --- a/addons/base_crypt/__terp__.py +++ b/addons/base_crypt/__openerp__.py @@ -26,11 +26,7 @@ "category" : "Generic Modules/Base", "description": "Module for password encryption.", "depends" : ["base"], - "init_xml" : [], - "demo_xml" : [], - "update_xml" : [ - "base_update.xml", - ], + "data" : [], "active": False, "installable": True, } diff --git a/addons/base_crypt/base_update.xml b/addons/base_crypt/base_update.xml index 66e4eaeb8c7..2744053a61c 100755 --- a/addons/base_crypt/base_update.xml +++ b/addons/base_crypt/base_update.xml @@ -2,6 +2,8 @@ + + res.users.form.modif.inherit res.users @@ -10,7 +12,7 @@ - + @@ -21,7 +23,7 @@ - + diff --git a/addons/base_crypt/crypt.py b/addons/base_crypt/crypt.py index b6a6ea4e3c5..4c03442b9d0 100755 --- a/addons/base_crypt/crypt.py +++ b/addons/base_crypt/crypt.py @@ -123,43 +123,65 @@ def encrypt_md5( raw_pw, salt, magic=magic_md5 ): return magic + salt + '$' + rearranged -#def check_super(passwd): -# salt = _salt_cache[passwd] -# if encrypt_md5( passwd, salt ) == tools.config['admin_passwd']: -# return True -# else: -# raise Exception('AccessDenied') - class users(osv.osv): _name="res.users" _inherit="res.users" # agi - 022108 # Add handlers for 'input_pw' field. + # Maps a res_users id to the salt used to encrypt its associated password. _salt_cache = {} - def set_pw( self, cr, uid, id, name, value, args, context ): + def set_pw(self, cr, uid, id, name, value, args, context): + print ">>>>>> set_pw %s" % str((self, cr, uid, id, name, value, args, context)) if not value: raise osv.except_osv(_('Error'), _("Please specify the password !")) - self.write( cr, uid, id, { 'password' : encrypt_md5( value, gen_salt() ) } ) + + salt = self._salt_cache[id] = gen_salt() + encrypted = encrypt_md5(value, salt) + cr.execute('update res_users set password=%s where id=%s', + (encrypted.encode('utf-8'), id)) + cr.commit() del value def get_pw( self, cr, uid, ids, name, args, context ): + print ">>>>>> get_pw" + if len(ids) != 1: + # TODO multiple ids (and no id) + return {} + id = ids[0] + + cr.execute('select password from res_users where id=%s', (id,)) + stored_pw = cr.fetchone() + + if stored_pw: + stored_pw = stored_pw[0] + else: + # Return early if no such id. + return False + + stored_pw = self.maybe_encrypt_and_store(cr, stored_pw, id) + res = {} - for id in ids: - res[id] = '' + res[id] = stored_pw return res - # Continuing to original code. - _columns = { - 'input_pw': fields.function( get_pw, fnct_inv=set_pw, type='char', method=True, size=20, string='Password', invisible=True), - } + # The column size could be smaller as it is meant to store a hash, but + # an existing column cannot be downsized; thus we use the original + # column size. + 'password': fields.function(get_pw, fnct_inv=set_pw, type='char', + method=True, size=64, string='Password', invisible=True, + store=True), + } + # TODO This doesn't seem right: _salt_cache doesn't necessarily contain uid. def access(self, db, uid, passwd, sec_level, ids): + print ">>>>>> access" cr = pooler.get_db(db).cursor() - salt = self._salt_cache[passwd] - cr.execute('select id from res_users where id=%s and password=%s', (uid, encrypt_md5( passwd, salt )) ) + salt = self._salt_cache[uid] + cr.execute('select id from res_users where id=%s and password=%s', + (uid, encrypt_md5(passwd, salt))) res = cr.fetchone() cr.close() if not res: @@ -167,39 +189,28 @@ class users(osv.osv): return res[0] def login(self, db, login, password): + print ">>>>>> login" cr = pooler.get_db(db).cursor() - cr.execute( 'select password from res_users where login=%s', (login.encode( 'utf-8' ),) ) - stored_pw = cr.fetchone() + cr.execute('select password, id from res_users where login=%s', + (login.encode( 'utf-8' ),)) + stored_pw = id = cr.fetchone() if stored_pw: stored_pw = stored_pw[0] + id = id[1] else: - # Return early if no one has a login name like that. + # Return early if there is no such login. return False - # Calculate a new password ('updated_pw') from 'stored_pw' if the - # latter isn't encrypted yet. Use that to update the database entry. - # Also update the 'stored_pw' to reflect the change. - - # TODO use length(magic_md5) instead of 3. - if stored_pw[0:3] != magic_md5: - updated_pw = encrypt_md5( stored_pw, gen_salt() ) - cr.execute( 'update res_users set password=%s where login=%s', (updated_pw.encode( 'utf-8' ), login.encode( 'utf-8' ),) ) - cr.commit() - - cr.execute( 'select password from res_users where login=%s', (login.encode( 'utf-8' ),) ) - stored_pw = cr.fetchone()[0] + stored_pw = self.maybe_encrypt_and_store(cr, stored_pw, id) # Calculate an encrypted password from the user-provided - # password ('encrypted_pw'). - - salt = self._salt_cache[password] = stored_pw[3:11] - encrypted_pw = encrypt_md5( password, salt ) - - # Retrieve a user id from the database, factoring in an encrypted # password. + salt = self._salt_cache[id] = stored_pw[len(magic_md5):11] + encrypted_pw = encrypt_md5(password, salt) - cr.execute('select id from res_users where login=%s and password=%s and active', (login.encode('utf-8'), encrypted_pw.encode('utf-8'))) + # Check if the encrypted password matches against the one in the db. + cr.execute('select id from res_users where id=%s and password=%s and active', (id, encrypted_pw.encode('utf-8'))) res = cr.fetchone() cr.close() @@ -209,28 +220,52 @@ class users(osv.osv): return False def check(self, db, uid, passwd): - # TODO see in self._uid_cache[db][uid] instead of - #if security._uid_cache.has_key( uid ) and (security._uid_cache[uid]==passwd): + print ">>>>>> check" + # TODO cannot use the cache as it would prevent the update by + # maybe_encrypt_and_store. + #cached_pass = self._uid_cache.get(db, {}).get(uid) + #if (cached_pass is not None) and cached_pass == passwd: # return True + cr = pooler.get_db(db).cursor() - if passwd not in self._salt_cache: - cr.execute( 'select login from res_users where id=%s', (int(uid),) ) + if uid not in self._salt_cache: + # TODO is int() useful ? + cr.execute('select login from res_users where id=%s', (int(uid),)) stored_login = cr.fetchone() if stored_login: stored_login = stored_login[0] - if not login(db,stored_login,passwd): + if not self.login(db,stored_login,passwd): return False - salt = self._salt_cache[passwd] - cr.execute(' select count(id) from res_users where id=%s and password=%s', (int(uid), encrypt_md5( passwd, salt )) ) + + salt = self._salt_cache[uid] + cr.execute('select count(id) from res_users where id=%s and password=%s', + (int(uid), encrypt_md5(passwd, salt))) res = cr.fetchone()[0] cr.close() if not bool(res): raise Exception('AccessDenied') - # TODO see above + #if res: - # security._uid_cache[uid] = passwd + # if self._uid_cache.has_key(db): + # ulist = self._uid_cache[db] + # ulist[uid] = passwd + # else: + # self._uid_cache[db] = {uid: passwd} return bool(res) + def maybe_encrypt_and_store(self, cr, pw, id): + # Calculate a new password 'encrypted' from 'pw' if the + # latter isn't encrypted yet. Use it to update the database entry + # and return it, or simply return 'pw'. + + if pw[0:len(magic_md5)] != magic_md5: + encrypted = encrypt_md5(pw, gen_salt()) + cr.execute('update res_users set password=%s where id=%s', + (encrypted.encode('utf-8'), id)) + cr.commit() + return encrypted + return pw + users() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: