Merge lp:~camptocamp/ocb-addons/improve_auth_crypt-nbi into lp:ocb-addons

Proposed by Nicolas Bessi - Camptocamp
Status: Merged
Approved by: Stefan Rijnhart (Opener)
Approved revision: no longer in the source branch.
Merged at revision: 10145
Proposed branch: lp:~camptocamp/ocb-addons/improve_auth_crypt-nbi
Merge into: lp:ocb-addons
Diff against target: 67 lines (+30/-6)
1 file modified
auth_crypt/auth_crypt.py (+30/-6)
To merge this branch: bzr merge lp:~camptocamp/ocb-addons/improve_auth_crypt-nbi
Reviewer Review Type Date Requested Status
Laurent Mignon (Acsone) (community) Approve
Holger Brunn (Therp) code review Approve
Stefan Rijnhart (Opener) Pending
Alexandre Fayolle - camptocamp code review, no test Pending
Nicolas Bessi - Camptocamp Pending
Review via email: mp+211750@code.launchpad.net

This proposal supersedes a proposal from 2014-02-14.

Description of the change

(Improve module auth_crypt use sha256 by default to encrypt password. The modification keeps retro compatibility.) REMOVED as OpenERP will not merge this part

Add an init function on res.users to encrypt all passwords when installing module and avoid plain password for deactivated users.

To post a comment you must log in.
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : Posted in a previous version of this proposal

code LGTM. Not tested.

review: Approve (code review, no test)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

This works, and I appreciate the contribution. However, if I understand correctly this breaks compatibility with standard OpenERP as well as things like OpenUpgrade so I am not in favour of merging this until it is actually merged in trunk (and there is no proposal yet on trunk).

review: Disapprove
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : Posted in a previous version of this proposal

Hello,

This will not breaks compatibility with OpenERP standards as the magic method key is encoded in plain at the beginning of the password. OpenERP will choose the correct way to decrypt password md5 or sha256 as said in comment.

Also I do not see in how it will break OpenUpgrade without further explaination I can only ask you to reconcider your review.

Regards

Nicolas

review: Approve
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Hi Nicolas,

consider the following scenario:

merge this branch, and install auth_crypt on an existing database. Passwords are converted to sha256. I can log on without a problem. Then revert the branch (or try using the database on a standard OpenERP branch, or a derivative like OpenUpgrade). User cannot logon because sha256 authentication is not available.

BTW. I tried this with a newly created user and then it seemed to work, but only because the passwords of new users are still hashed with md5. So your branch needs a little fix in set_pw, I reckon.

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : Posted in a previous version of this proposal

Hello,

Ok, I get it know :).

I will propose the MP on official branch and see if tey accept it.

If they don't I will do a MP that will only fix the fact that deactivated user are not encrypted.
And that will encrypt in all clear password in md5.

Do you agree with that?

Regards

Nicolas.

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Excellent, thanks!

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : Posted in a previous version of this proposal

Hello,

MP is modified as disscussed with Olivier only init function will be accepted.
The same patch is proposed on official addons

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Hi Nicolas, did you not want to take Olivier's suggestion of refactoring out the code that performs the encryption?

review: Needs Information
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : Posted in a previous version of this proposal

Hello Stefan,

I prefer to do it in an other MP, in order to fix the case of plain password as soon as possible.

Also the refactoring using the passlib will probably be done against the trunk branch as there is some difference in implementation and I'm not sure at the current time they are fully compatible.

Regards

Nicolas

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

OK, thanks for the info!

review: Approve
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : Posted in a previous version of this proposal

Ho I got your point Stefan,

I missed the answer of Olivier on addons MP

For this part, your init() method looks fine, but there are already multiple instances of the salting+hashing dance. As you're adding one more, it seems a good opportunity to refactor a bit and extract that pattern into a private method, something like:

def _set_user_password(self, cr, uid, user_id, password, context=None):
     password_hash = md5_crypt(password, gen_salt()) # TODO: update default algo in trunk
     cr.execute("UPDATE res_users SET password='', password_crypt=%s WHERE id=%s",
                (password_hash, user_id))

Yes I should do it

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : Posted in a previous version of this proposal

I have done the small refactor proposed by Olivier (not the one with passlib).

Please redo a quick review before merging.

Regards

Nicolas

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote : Posted in a previous version of this proposal

LGTM!

Better to have only one method that makes the encryption.

Thanks.

review: Approve (code review)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

LGTM, thanks!

review: Approve
Revision history for this message
Holger Brunn (Therp) (hbrunn) :
review: Approve (code review)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Setting to approved, as I count three approvals after the last change, even if that was in the superseded MP.

Revision history for this message
Laurent Mignon (Acsone) (lmi) wrote :

Too late... but I've the feeling that the code will break existing installations on the second call to the upgrade method of the module. The init method is called each time the module is upgraded. IMO, this kink of logic should be put in a post-migration script.
What's happening if you execute 2 times the command 'start_openerp -u all' on a existing database with the module installed? I think that the passwords will be encrypted 2 times.
Hope I'm wrong...

review: Disapprove
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

Actually, init() is called every time you start your server. But take a closer look at the columns in question: The plain text password lives in password, while the encrypted one goes to password_crypt. When encrypting a password, we fill password_crypt and clear password, so we won't run into problems here.

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

...and unfortunately, migration scripts are only called for updating a module, not when you install them, no matter what version you install

Revision history for this message
Laurent Mignon (Acsone) (lmi) wrote :

Holger,

Thanks for information and sorry for the noise.
About, migration script, last time I've debugged a post_migration script, If I remember correctly, at installation the script was called with a version set to None.

Regards,

lmi

review: Approve
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

No problem.

I'll investigate about the migration script, I couldn't get mine to run for a freshly installed addon, seems like I messed up somewhere

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

FYI: I'm attempting to make a migration script run upon the installation of another module in 7.0, and it does not seem to work. It must have worked in earlier versions of OpenERP, because we did take special precautions early on in OpenUpgrade to prevent migration scripts to run upon module installation (with version=None indeed).

Revision history for this message
Laurent Mignon (Acsone) (lmi) wrote :

Hi,

I made ​​the same observation on the SAAS-X branches and reported a bug in launchpad https://bugs.launchpad.net/openobject-server/+bug/1314680 since, for me, this change remove a useful functionality of the MigrationManager in openerp. The change referenced in the bug description hasn't been backported in the 7.0 branches. I don't understand the motivations behind this change.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'auth_crypt/auth_crypt.py'
2--- auth_crypt/auth_crypt.py 2013-08-12 10:29:50 +0000
3+++ auth_crypt/auth_crypt.py 2014-03-19 14:54:21 +0000
4@@ -105,6 +105,7 @@
5
6 return magic + salt + '$' + rearranged
7
8+
9 def sh256crypt(cls, password, salt, magic=magic_sha256):
10 iterations = 1000
11 # see http://en.wikipedia.org/wiki/PBKDF2
12@@ -114,16 +115,41 @@
13 result = result.encode('base64') # doesnt seem to be crypt(3) compatible
14 return '%s%s$%s' % (magic_sha256, salt, result)
15
16+
17 class res_users(osv.osv):
18 _inherit = "res.users"
19
20+ def init(self, cr):
21+ """Encrypt all passwords at module installation"""
22+ cr.execute("SELECT id, password FROM res_users WHERE password != ''",)
23+ to_encrypt = cr.fetchall()
24+ if to_encrypt:
25+ for user in to_encrypt:
26+ self._set_encrypted_password(cr, user[0], user[1])
27+ return True
28+
29+ def _set_encrypted_password(self, cr, user_id, plain_password):
30+ """Set an encrypted password for a given user
31+
32+ :param cr: database cursor
33+ :param user_id: user_id
34+ :param plain_password: plain password to encrypt
35+
36+ :returns: user id
37+
38+ """
39+ salt = gen_salt()
40+ stored_password_crypt = md5crypt(plain_password, salt)
41+ cr.execute("UPDATE res_users SET password='', password_crypt=%s WHERE id=%s",
42+ (stored_password_crypt, user_id))
43+ return user_id
44+
45 def set_pw(self, cr, uid, id, name, value, args, context):
46 if value:
47- encrypted = md5crypt(value, gen_salt())
48- cr.execute("update res_users set password='', password_crypt=%s where id=%s", (encrypted, id))
49+ self._set_encrypted_password(cr, id, value)
50 del value
51
52- def get_pw( self, cr, uid, ids, name, args, context ):
53+ def get_pw(self, cr, uid, ids, name, args, context):
54 cr.execute('select id, password from res_users where id in %s', (tuple(map(int, ids)),))
55 stored_pws = cr.fetchall()
56 res = {}
57@@ -144,9 +170,7 @@
58 if cr.rowcount:
59 stored_password, stored_password_crypt = cr.fetchone()
60 if stored_password and not stored_password_crypt:
61- salt = gen_salt()
62- stored_password_crypt = md5crypt(stored_password, salt)
63- cr.execute("UPDATE res_users SET password='', password_crypt=%s WHERE id=%s", (stored_password_crypt, uid))
64+ self._set_encrypted_password(cr, uid, stored_password)
65 try:
66 return super(res_users, self).check_credentials(cr, uid, password)
67 except openerp.exceptions.AccessDenied: