Module for password encryption.
[odoo/odoo.git] / addons / base_crypt / crypt.py
1 from random import seed, sample
2 from string import letters, digits
3 from osv import fields,osv
4 import pooler
5 import tools
6 from service import security
7
8 magic_md5 = '$1$'
9
10 def gen_salt( length=8, symbols=letters + digits ):
11     seed()
12     return ''.join( sample( symbols, length ) )
13
14 import md5
15
16 def encrypt_md5( raw_pw, salt, magic=magic_md5 ):
17     hash = md5.new( raw_pw + magic + salt )
18     stretch = md5.new( raw_pw + salt + raw_pw).digest()
19
20     for i in range( 0, len( raw_pw ) ):
21         hash.update( stretch[i % 16] )
22
23     i = len( raw_pw )
24
25     while i:
26         if i & 1:
27             hash.update('\x00')
28         else:
29             hash.update( raw_pw[0] )
30         i >>= 1
31
32     saltedmd5 = hash.digest()
33
34     for i in range( 1000 ):
35         hash = md5.new()
36
37         if i & 1:
38             hash.update( raw_pw )
39         else:
40             hash.update( saltedmd5 )
41
42         if i % 3:
43             hash.update( salt )
44         if i % 7:
45             hash.update( raw_pw )
46         if i & 1:
47             hash.update( saltedmd5 )
48         else:
49             hash.update( raw_pw )
50
51         saltedmd5 = hash.digest()
52
53     itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
54
55     rearranged = ''
56     for a, b, c in ((0, 6, 12), (1, 7, 13), (2, 8, 14), (3, 9, 15), (4, 10, 5)):
57         v = ord( saltedmd5[a] ) << 16 | ord( saltedmd5[b] ) << 8 | ord( saltedmd5[c] )
58
59         for i in range(4):
60             rearranged += itoa64[v & 0x3f]
61             v >>= 6
62
63     v = ord( saltedmd5[11] )
64
65     for i in range( 2 ):
66         rearranged += itoa64[v & 0x3f]
67         v >>= 6
68
69     return magic + salt + '$' + rearranged
70
71 _salt_cache = {}
72
73 def login(db, login, password):
74     cr = pooler.get_db(db).cursor()
75     cr.execute( 'select password from res_users where login=%s', (login.encode( 'utf-8' ),) )
76     stored_pw = cr.fetchone()
77
78     if stored_pw:
79         stored_pw = stored_pw[0]
80     else:
81         # Return early if no one has a login name like that.
82         return False
83
84     # Calculate a new password ('updated_pw') from 'stored_pw' if the
85     # latter isn't encrypted yet. Use that to update the database entry.
86     # Also update the 'stored_pw' to reflect the change.
87
88     if stored_pw[0:3] != magic_md5:
89         updated_pw = encrypt_md5( stored_pw, gen_salt() )
90         cr.execute( 'update res_users set password=%s where login=%s', (updated_pw.encode( 'utf-8' ), login.encode( 'utf-8' ),) )
91         cr.commit()
92
93         cr.execute( 'select password from res_users where login=%s', (login.encode( 'utf-8' ),) )
94         stored_pw = cr.fetchone()[0]
95
96     # Calculate an encrypted password from the user-provided
97     # password ('encrypted_pw').
98
99     salt = _salt_cache[password] = stored_pw[3:11]
100     encrypted_pw = encrypt_md5( password, salt )
101
102     # Retrieve a user id from the database, factoring in an encrypted
103     # password.
104
105     cr.execute('select id from res_users where login=%s and password=%s and active', (login.encode('utf-8'), encrypted_pw.encode('utf-8')))
106     res = cr.fetchone()
107     cr.close()
108
109     if res:
110         return res[0]
111     else:
112         return False
113
114 #def check_super(passwd):
115 #    salt = _salt_cache[passwd]
116 #    if encrypt_md5( passwd, salt ) == tools.config['admin_passwd']:
117 #         return True
118 #    else:
119 #         raise Exception('AccessDenied')
120
121 def check(db, uid, passwd):
122     if security._uid_cache.has_key( uid ) and (security._uid_cache[uid]==passwd):
123         return True
124     cr = pooler.get_db(db).cursor()
125     salt = _salt_cache[passwd]
126     cr.execute(' select count(*) from res_users where id=%d and password=%s', (int(uid), encrypt_md5( passwd, salt )) )
127     res = cr.fetchone()[0]
128     cr.close()
129     if not bool(res):
130         raise Exception('AccessDenied')
131     if res:
132         security._uid_cache[uid] = passwd
133     return bool(res)
134
135
136 def access(db, uid, passwd, sec_level, ids):
137     cr = pooler.get_db(db).cursor()
138     salt = _salt_cache[passwd]
139     cr.execute('select id from res_users where id=%s and password=%s', (uid, encrypt_md5( passwd, salt )) )
140     res = cr.fetchone()
141     cr.close()
142     if not res:
143         raise Exception('Bad username or password')
144     return res[0]
145
146 # check if module is installed or not
147 security.login=login
148 #security.check_super=check_super
149 security.access=access
150 security.check=check
151
152 class users(osv.osv):
153     _name="res.users"
154     _inherit="res.users"
155     # agi - 022108
156     # Add handlers for 'input_pw' field.
157
158     def set_pw( self, cr, uid, id, name, value, args, context ):
159         self.write( cr, uid, id, { 'password' : encrypt_md5( value, gen_salt() ) } )
160         del value
161
162     def get_pw( self, cr, uid, ids, name, args, context ):
163         res = {}
164         for id in ids:
165             res[id] = ''
166         return res
167
168     # Continuing to original code.
169
170     _columns = {
171         'input_pw': fields.function( get_pw, fnct_inv=set_pw, type='char', method=True, size=20, string='Password', invisible=True),
172             }
173 users()