[FIX] OPW 576675: base_crypt: avoid causing TransactionRollbackErrors after each registry reload
See individual commit message for more info bzr revid: odo@openerp.com-20120813151648-gvxggkz0dnj3y6ax
This commit is contained in:
+42
-51
@@ -35,9 +35,13 @@
|
||||
# 59 Temple Place - Suite 330
|
||||
# Boston, MA 02111-1307
|
||||
# USA.
|
||||
from __future__ import with_statement
|
||||
|
||||
from contextlib import closing
|
||||
import logging
|
||||
from random import seed, sample
|
||||
from string import ascii_letters, digits
|
||||
|
||||
from osv import fields,osv
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
@@ -135,6 +139,20 @@ class users(osv.osv):
|
||||
# agi - 022108
|
||||
# Add handlers for 'input_pw' field.
|
||||
|
||||
def init(self, cr):
|
||||
cr.execute("SELECT id, password FROM res_users "
|
||||
"WHERE active=true AND password NOT LIKE '$%' ")
|
||||
to_crypt = cr.fetchall()
|
||||
if to_crypt:
|
||||
# Full table lock to guarantee we won't do a partial job or
|
||||
# end up with a TransactionRollbackError if another transaction
|
||||
# alters res_users in the mean time. The lock will be released
|
||||
# by the commit() that follows the model initialization step
|
||||
cr.execute('LOCK res_users')
|
||||
cr.executemany("UPDATE res_users SET password=%s WHERE id=%s",
|
||||
((encrypt_md5(password, gen_salt()), id)
|
||||
for id, password in to_crypt))
|
||||
|
||||
def set_pw(self, cr, uid, id, name, value, args, context):
|
||||
if not value:
|
||||
raise osv.except_osv(_('Error'), _("Please specify the password !"))
|
||||
@@ -174,17 +192,13 @@ class users(osv.osv):
|
||||
return False
|
||||
if db is False:
|
||||
raise RuntimeError("Cannot authenticate to False db!")
|
||||
cr = None
|
||||
|
||||
try:
|
||||
cr = pooler.get_db(db).cursor()
|
||||
return self._login(cr, db, login, password)
|
||||
with closing(pooler.get_db(db).cursor()) as cr:
|
||||
return self._login(cr, db, login, password)
|
||||
except Exception:
|
||||
import logging
|
||||
logging.getLogger('netsvc').exception('Could not authenticate')
|
||||
return Exception('Access Denied')
|
||||
finally:
|
||||
if cr is not None:
|
||||
cr.close()
|
||||
|
||||
def _login(self, cr, db, login, password):
|
||||
cr.execute( 'SELECT password, id FROM res_users WHERE login=%s AND active',
|
||||
@@ -196,12 +210,6 @@ class users(osv.osv):
|
||||
# Return early if no one has a login name like that.
|
||||
return False
|
||||
|
||||
stored_pw = self.maybe_encrypt(cr, stored_pw, id)
|
||||
|
||||
if not stored_pw:
|
||||
# means couldn't encrypt or user is not active!
|
||||
return False
|
||||
|
||||
# Calculate an encrypted password from the user-provided
|
||||
# password.
|
||||
obj = pooler.get_pool(db).get('res.users')
|
||||
@@ -211,18 +219,25 @@ class users(osv.osv):
|
||||
encrypted_pw = encrypt_md5(password, salt)
|
||||
|
||||
# Check if the encrypted password matches against the one in the db.
|
||||
cr.execute("""UPDATE res_users
|
||||
SET date=now() AT TIME ZONE 'UTC'
|
||||
WHERE id=%s AND password=%s AND active
|
||||
RETURNING id""",
|
||||
(int(id), encrypted_pw.encode('utf-8')))
|
||||
res = cr.fetchone()
|
||||
cr.commit()
|
||||
login_ok = encrypted_pw == stored_pw
|
||||
|
||||
# Attempt to update last login time, but don't fail if the row is
|
||||
# currently locked.
|
||||
if login_ok:
|
||||
try:
|
||||
cr.execute("SELECT 1 FROM res_users WHERE id=%s FOR UPDATE NOWAIT",
|
||||
params=(id,), log_exceptions=False)
|
||||
cr.execute("""UPDATE res_users
|
||||
SET date=now() AT TIME ZONE 'UTC'
|
||||
WHERE id=%s""", (id,))
|
||||
cr.commit()
|
||||
except Exception:
|
||||
# Failing to acquire the lock on the res_users row probably means
|
||||
# another request is holding it. No big deal, we don't want to
|
||||
# prevent/delay login in that case just for updating login timestamp.
|
||||
cr.rollback()
|
||||
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return False
|
||||
return login_ok and id
|
||||
|
||||
def check(self, db, uid, passwd):
|
||||
if not passwd:
|
||||
@@ -239,8 +254,9 @@ class users(osv.osv):
|
||||
if (cached_pass is not None) and cached_pass == passwd:
|
||||
return True
|
||||
|
||||
cr = pooler.get_db(db).cursor()
|
||||
try:
|
||||
# We should get here only if the registry was just reloaded
|
||||
# (e.g. after server startup or module install/upgrade)
|
||||
with closing(pooler.get_db(db).cursor()) as cr:
|
||||
if uid not in self._salt_cache.get(db, {}):
|
||||
# If we don't have cache, we have to repeat the procedure
|
||||
# through the login function.
|
||||
@@ -257,8 +273,6 @@ class users(osv.osv):
|
||||
cr.execute('SELECT COUNT(*) FROM res_users WHERE id=%s AND password=%s AND active',
|
||||
(int(uid), encrypt_md5(passwd, salt)))
|
||||
res = cr.fetchone()[0]
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
if not bool(res):
|
||||
raise security.ExceptionNoTb('AccessDenied')
|
||||
@@ -270,29 +284,6 @@ class users(osv.osv):
|
||||
else:
|
||||
self._uid_cache[db] = {uid: passwd}
|
||||
return bool(res)
|
||||
|
||||
def maybe_encrypt(self, cr, pw, id):
|
||||
""" Return the password 'pw', making sure it is encrypted.
|
||||
|
||||
If the password 'pw' is not encrypted, then encrypt all active passwords
|
||||
in the db. Returns the (possibly newly) encrypted password for 'id'.
|
||||
"""
|
||||
|
||||
if not pw.startswith(magic_md5):
|
||||
cr.execute("SELECT id, password FROM res_users " \
|
||||
"WHERE active=true AND password NOT LIKE '$%'")
|
||||
# Note that we skip all passwords like $.., in anticipation for
|
||||
# more than md5 magic prefixes.
|
||||
res = cr.fetchall()
|
||||
for i, p in res:
|
||||
encrypted = encrypt_md5(p, gen_salt())
|
||||
cr.execute('UPDATE res_users SET password=%s where id=%s',
|
||||
(encrypted, i))
|
||||
if i == id:
|
||||
encrypted_res = encrypted
|
||||
cr.commit()
|
||||
return encrypted_res
|
||||
return pw
|
||||
|
||||
users()
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
Reference in New Issue
Block a user