Merge lp:~fabien-morin/unifield-server/fm-us-1381-1802-1651-1763 into lp:unifield-server
- fm-us-1381-1802-1651-1763
- Merge into trunk
Proposed by
jftempo
Status: | Merged |
---|---|
Merged at revision: | 4079 |
Proposed branch: | lp:~fabien-morin/unifield-server/fm-us-1381-1802-1651-1763 |
Merge into: | lp:unifield-server |
Diff against target: |
1094 lines (+536/-174) (has conflicts) 12 files modified
bin/addons/base/base_update.xml (+8/-0) bin/addons/base/res/res_user.py (+200/-94) bin/addons/msf_profile/data/patches.xml (+7/-0) bin/addons/msf_profile/i18n/fr_MF.po (+81/-0) bin/addons/msf_profile/msf_profile.py (+18/-0) bin/addons/msf_profile/user_access_configurator_view.xml (+1/-0) bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+1/-0) bin/addons/sync_client/rpc.py (+3/-0) bin/openerp-server.py (+22/-19) bin/service/security.py (+134/-39) bin/service/web_services.py (+57/-22) setup.py (+4/-0) Text conflict in bin/addons/msf_profile/data/patches.xml Text conflict in bin/addons/msf_profile/i18n/fr_MF.po Text conflict in bin/addons/msf_profile/msf_profile.py Text conflict in bin/addons/sync_client/rpc.py Text conflict in bin/service/web_services.py Text conflict in setup.py |
To merge this branch: | bzr merge lp:~fabien-morin/unifield-server/fm-us-1381-1802-1651-1763 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email: mp+309126@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 3996. By Fabien MORIN
-
US-1381 [IMP] comment the synchronize checkbox because the US-1381 is
postponned.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/addons/base/base_update.xml' |
2 | --- bin/addons/base/base_update.xml 2016-06-15 09:19:59 +0000 |
3 | +++ bin/addons/base/base_update.xml 2016-11-21 09:47:14 +0000 |
4 | @@ -111,6 +111,12 @@ |
5 | <field name="login" select="1"/> |
6 | <field name="new_password" password="True"/> |
7 | <field name="force_password_change"/> |
8 | + <!-- this functionnality is linked to US-1381. As this |
9 | + ticket was postponed to UF2.1-5, this checkbox will not be displayed until this ticket is not released |
10 | + <field name="synchronize" attrs="{'invisible': ['|', '|', ('is_erp_manager', '=', True), ('is_sync_config', '=', True), ('instance_level', '!=', 'section')]}"/>--> |
11 | + <field name="is_erp_manager" invisible="1"/> |
12 | + <field name="is_sync_config" invisible="1"/> |
13 | + <field name="instance_level" invisible="1"/> |
14 | <newline/> |
15 | <notebook colspan="4"> |
16 | <page string="User"> |
17 | @@ -164,6 +170,7 @@ |
18 | <field name="login"/> |
19 | <field name="context_lang"/> |
20 | <field name="date"/> |
21 | + <field name="synchronize"/> |
22 | </tree> |
23 | </field> |
24 | </record> |
25 | @@ -178,6 +185,7 @@ |
26 | <field name="login"/> |
27 | <field name="address_id" string="Address"/> |
28 | <field name="company_ids" string="Company" groups="base.group_multi_company"/> |
29 | + <field name="synchronize"/> |
30 | </search> |
31 | </field> |
32 | </record> |
33 | |
34 | === modified file 'bin/addons/base/res/res_user.py' |
35 | --- bin/addons/base/res/res_user.py 2016-10-04 15:38:43 +0000 |
36 | +++ bin/addons/base/res/res_user.py 2016-11-21 09:47:14 +0000 |
37 | @@ -30,7 +30,7 @@ |
38 | from service import security |
39 | import netsvc |
40 | import logging |
41 | -import re |
42 | +from passlib.hash import bcrypt |
43 | |
44 | class groups(osv.osv): |
45 | _name = "res.groups" |
46 | @@ -103,7 +103,6 @@ |
47 | _name = "res.users" |
48 | _order = 'name' |
49 | |
50 | - PASSWORD_MIN_LENGHT = 6 |
51 | WELCOME_MAIL_SUBJECT = u"Welcome to OpenERP" |
52 | WELCOME_MAIL_BODY = u"An OpenERP account has been created for you, "\ |
53 | "\"%(name)s\".\n\nYour login is %(login)s, "\ |
54 | @@ -135,7 +134,7 @@ |
55 | |
56 | def send_welcome_email(self, cr, uid, id, context=None): |
57 | logger= netsvc.Logger() |
58 | - user = self.pool.get('res.users').read(cr, uid, id, context=context) |
59 | + user = self.read(cr, uid, id, context=context) |
60 | if not tools.config.get('smtp_server'): |
61 | logger.notifyChannel('mails', netsvc.LOG_WARNING, |
62 | _('"smtp_server" needs to be set to send mails to users')) |
63 | @@ -214,11 +213,69 @@ |
64 | # so that the new password is immediately used for further RPC requests, otherwise the user |
65 | # will face unexpected 'Access Denied' exceptions. |
66 | raise osv.except_osv(_('Operation Canceled'), _('Please use the change password wizard (in User Preferences or User menu) to change your own password.')) |
67 | - if not all(self.is_password_strong(value, login).values()): |
68 | - raise osv.except_osv(_('Operation Canceled'), _('The new password is not strong enough. '\ |
69 | - 'Password must be different from the login, it must contain '\ |
70 | - 'at least one number and be at least %s characters.' % self.PASSWORD_MIN_LENGHT)) |
71 | - self.write(cr, uid, id, {'password': value}) |
72 | + security.check_password_validity(self, cr, uid, None, value, value, login) |
73 | + encrypted_password = bcrypt.encrypt(tools.ustr(value)) |
74 | + self.write(cr, uid, id, {'password': encrypted_password}) |
75 | + |
76 | + def _is_erp_manager(self, cr, uid, ids, name=None, arg=None, context=None): |
77 | + ''' |
78 | + return True if the user is member of the group_erp_manager (usually, |
79 | + admin of the site). |
80 | + ''' |
81 | + if isinstance(ids, (int, long)): |
82 | + ids = [ids] |
83 | + manager_group_id = None |
84 | + result = dict.fromkeys(ids, False) |
85 | + try: |
86 | + dataobj = self.pool.get('ir.model.data') |
87 | + dummy, manager_group_id = dataobj.get_object_reference(cr, 1, 'base', |
88 | + 'group_erp_manager') |
89 | + except ValueError: |
90 | + # If these groups does not exists anymore |
91 | + pass |
92 | + if manager_group_id: |
93 | + read_result = self.read(cr, uid, ids, ['groups_id'], context=context) |
94 | + for current_user in read_result: |
95 | + if manager_group_id in current_user['groups_id']: |
96 | + result[current_user['id']] = True |
97 | + return result |
98 | + |
99 | + def _is_sync_config(self, cr, uid, ids, name=None, arg=None, context=None): |
100 | + ''' |
101 | + return True if the user is member of the Sync_Config |
102 | + ''' |
103 | + if isinstance(ids, (int, long)): |
104 | + ids = [ids] |
105 | + result = dict.fromkeys(ids, False) |
106 | + group_id = None |
107 | + res_group_obj = self.pool.get('res.groups') |
108 | + group_ids = res_group_obj.search(cr, uid, |
109 | + [('name', '=', 'Sync_Config')], context=context) |
110 | + if group_ids: |
111 | + group_id = group_ids[0] |
112 | + read_result = self.read(cr, uid, ids, ['groups_id'], context=context) |
113 | + for current_user in read_result: |
114 | + if group_id in current_user['groups_id']: |
115 | + result[current_user['id']] = True |
116 | + return result |
117 | + |
118 | + def _get_instance_level(self, cr, uid, ids, name=None, arg=None, context=None): |
119 | + ''' |
120 | + return the level of the instance related to the company of the user |
121 | + ''' |
122 | + if isinstance(ids, (int, long)): |
123 | + ids = [ids] |
124 | + result = {} |
125 | + for user_id in ids: |
126 | + level = False |
127 | + company_id = self._get_company(cr, user_id, context=context) |
128 | + instance_id = self.pool.get('res.company').read(cr, uid, company_id, |
129 | + ['instance_id'], context=context)['instance_id'] |
130 | + instance_id = instance_id and instance_id[0] or False |
131 | + if instance_id: |
132 | + level = self.pool.get('msf.instance').read(cr, uid, instance_id, ['level'], context=context)['level'] |
133 | + result[user_id] = level |
134 | + return result |
135 | |
136 | _columns = { |
137 | 'name': fields.char('User Name', size=64, required=True, select=True, |
138 | @@ -226,7 +283,7 @@ |
139 | " and most listings"), |
140 | 'login': fields.char('Login', size=64, required=True, |
141 | help="Used to log into the system"), |
142 | - 'password': fields.char('Password', size=64, invisible=True, help="Keep empty if you don't want the user to be able to connect on the system."), |
143 | + 'password': fields.char('Password', size=128, invisible=True, help="Keep empty if you don't want the user to be able to connect on the system."), |
144 | 'new_password': fields.function(lambda *a:'', method=True, type='char', size=64, |
145 | fnct_inv=_set_new_password, |
146 | string='Change password', help="Only specify a value if you want to change the user password. " |
147 | @@ -265,6 +322,16 @@ |
148 | 'user_email': fields.function(_email_get, method=True, fnct_inv=_email_set, string='Email', type="char", size=240), |
149 | 'menu_tips': fields.boolean('Menu Tips', help="Check out this box if you want to always display tips on each menu action"), |
150 | 'date': fields.datetime('Last Connection', readonly=True), |
151 | + 'synchronize': fields.boolean('Synchronize', help="Synchronize down this user"), |
152 | + 'is_erp_manager': fields.function(_is_erp_manager, method=True, |
153 | + fnct_inv=lambda *a:'', string='Is ERP Manager ?', |
154 | + type="boolean"), |
155 | + 'is_sync_config': fields.function(_is_sync_config, method=True, |
156 | + fnct_inv=lambda *a:'', string='Is Sync Config ?', |
157 | + type="boolean"), |
158 | + 'instance_level': fields.function(_get_instance_level, method=True, |
159 | + fnct_inv=lambda *a:'', string='Instance level', |
160 | + type="char"), |
161 | } |
162 | |
163 | def on_change_company_id(self, cr, uid, ids, company_id): |
164 | @@ -277,17 +344,18 @@ |
165 | |
166 | def read(self,cr, uid, ids, fields=None, context=None, load='_classic_read'): |
167 | def override_password(o): |
168 | - if 'password' in o and ( 'id' not in o or o['id'] != uid ): |
169 | + if 'id' not in o or o['id'] != uid: |
170 | o['password'] = '********' |
171 | return o |
172 | |
173 | result = super(users, self).read(cr, uid, ids, fields, context, load) |
174 | - canwrite = self.pool.get('ir.model.access').check(cr, uid, 'res.users', 'write', raise_exception=False) |
175 | - if not canwrite: |
176 | - if isinstance(ids, (int, float)): |
177 | - result = override_password(result) |
178 | - else: |
179 | - result = map(override_password, result) |
180 | + if 'password' in result: |
181 | + canwrite = self.pool.get('ir.model.access').check(cr, uid, 'res.users', 'write', raise_exception=False) |
182 | + if not canwrite: |
183 | + if isinstance(ids, (int, float)): |
184 | + result = override_password(result) |
185 | + else: |
186 | + result = map(override_password, result) |
187 | return result |
188 | |
189 | |
190 | @@ -321,7 +389,7 @@ |
191 | def _get_company(self,cr, uid, context=None, uid2=False): |
192 | if not uid2: |
193 | uid2 = uid |
194 | - user = self.pool.get('res.users').read(cr, uid, uid2, ['company_id'], context) |
195 | + user = self.read(cr, uid, uid2, ['company_id'], context) |
196 | company_id = user.get('company_id', False) |
197 | return company_id and company_id[0] or False |
198 | |
199 | @@ -398,6 +466,13 @@ |
200 | |
201 | res = super(users, self).write(cr, uid, ids, values, context=context) |
202 | |
203 | + # uncheck synchronize checkbox if the user is manager or sync config |
204 | + if values.get('groups_id'): |
205 | + if any(self._is_sync_config(cr, uid, ids, context=context).values()) or\ |
206 | + any(self._is_erp_manager(cr, uid, ids, context=context).values()): |
207 | + vals = {'synchronize': False} |
208 | + res = super(users, self).write(cr, uid, ids, vals, context=context) |
209 | + |
210 | # clear caches linked to the users |
211 | self.company_get.clear_cache(cr.dbname) |
212 | self.pool.get('ir.model.access').call_cache_clearing_methods(cr) |
213 | @@ -461,51 +536,81 @@ |
214 | data_id = dataobj._get_id(cr, 1, 'base', 'action_res_users_my') |
215 | return dataobj.browse(cr, uid, data_id, context=context).res_id |
216 | |
217 | + def get_user_database_password_from_uid(self, cr, uid): |
218 | + ''' |
219 | + return encrypted password from the database using uid |
220 | + ''' |
221 | + cr.execute("""SELECT password from res_users |
222 | + WHERE id=%s AND active""", |
223 | + (uid,)) |
224 | + res = cr.fetchone() |
225 | + if res: |
226 | + return tools.ustr(res[0]) |
227 | + return False |
228 | + |
229 | + def get_user_database_password_from_login(self, cr, login): |
230 | + ''' |
231 | + return encrypted password from the database using login |
232 | + ''' |
233 | + login = tools.ustr(login).lower() |
234 | + cr.execute("""SELECT password from res_users |
235 | + WHERE login=%s AND active""", |
236 | + (login,)) |
237 | + res = cr.fetchone() |
238 | + if res: |
239 | + return tools.ustr(res[0]) |
240 | + return False |
241 | + |
242 | def login(self, db, login, password): |
243 | if not password: |
244 | return False |
245 | login = tools.ustr(login).lower() |
246 | cr = pooler.get_db(db).cursor() |
247 | try: |
248 | - # autocommit: our single request will be performed atomically. |
249 | - # (In this way, there is no opportunity to have two transactions |
250 | - # interleaving their cr.execute()..cr.commit() calls and have one |
251 | - # of them rolled back due to a concurrent access.) |
252 | - # We effectively unconditionally write the res_users line. |
253 | - cr.autocommit(True) |
254 | - # Even w/ autocommit there's a chance the user row will be locked, |
255 | - # in which case we can't delay the login just for the purpose of |
256 | - # update the last login date - hence we use FOR UPDATE NOWAIT to |
257 | - # try to get the lock - fail-fast |
258 | - cr.execute("""SELECT id from res_users |
259 | - WHERE login=%s AND password=%s |
260 | - AND active FOR UPDATE NOWAIT""", |
261 | - (login, tools.ustr(password)), log_exceptions=False) |
262 | - cr.execute('UPDATE res_users SET date=now() WHERE login=%s AND password=%s AND active RETURNING id', |
263 | - (login, tools.ustr(password))) |
264 | - except Exception: |
265 | - # Failing to acquire the lock on the res_users row probably means |
266 | - # another request is holding it - no big deal, we skip the update |
267 | - # for this time, and let the user login anyway. |
268 | - logging.getLogger('res.users').warn('Can\'t acquire lock on res users', exc_info=True) |
269 | - cr.rollback() |
270 | - cr.execute("""SELECT id from res_users |
271 | - WHERE login=%s AND password=%s |
272 | - AND active""", |
273 | - (login, tools.ustr(password))) |
274 | + database_password = self.get_user_database_password_from_login(cr, login) |
275 | + # check the password is a bcrypt encrypted one |
276 | + database_password = tools.ustr(database_password) |
277 | + password = tools.ustr(password) |
278 | + if bcrypt.identify(database_password): |
279 | + if not bcrypt.verify(password, database_password): |
280 | + return False |
281 | + elif password != database_password: |
282 | + return False |
283 | + try: |
284 | + # autocommit: our single request will be performed atomically. |
285 | + # (In this way, there is no opportunity to have two transactions |
286 | + # interleaving their cr.execute()..cr.commit() calls and have one |
287 | + # of them rolled back due to a concurrent access.) |
288 | + # We effectively unconditionally write the res_users line. |
289 | + cr.autocommit(True) |
290 | + # Even w/ autocommit there's a chance the user row will be locked, |
291 | + # in which case we can't delay the login just for the purpose of |
292 | + # update the last login date - hence we use FOR UPDATE NOWAIT to |
293 | + # try to get the lock - fail-fast |
294 | + cr.execute("""SELECT id from res_users |
295 | + WHERE login=%s AND password=%s |
296 | + AND active FOR UPDATE NOWAIT""", |
297 | + (login, tools.ustr(database_password)), log_exceptions=False) |
298 | + cr.execute('UPDATE res_users SET date=now() WHERE login=%s AND password=%s AND active RETURNING id', |
299 | + (login, tools.ustr(database_password))) |
300 | + except Exception: |
301 | + # Failing to acquire the lock on the res_users row probably means |
302 | + # another request is holding it - no big deal, we skip the update |
303 | + # for this time, and let the user login anyway. |
304 | + logging.getLogger('res.users').warn('Can\'t acquire lock on res users', exc_info=True) |
305 | + cr.rollback() |
306 | + cr.execute("""SELECT id from res_users |
307 | + WHERE login=%s AND password=%s |
308 | + AND active""", |
309 | + (login, tools.ustr(database_password))) |
310 | + finally: |
311 | + res = cr.fetchone() |
312 | + if res: |
313 | + return res[0] |
314 | finally: |
315 | - res = cr.fetchone() |
316 | cr.close() |
317 | - if res: |
318 | - return res[0] |
319 | return False |
320 | |
321 | - def check_super(self, passwd): |
322 | - if passwd == tools.config['admin_passwd']: |
323 | - return True |
324 | - else: |
325 | - raise security.ExceptionNoTb('AccessDenied') |
326 | - |
327 | def check(self, db, uid, passwd): |
328 | """Verifies that the given (uid, password) pair is authorized for the database ``db`` and |
329 | raise an exception if it is not.""" |
330 | @@ -516,11 +621,16 @@ |
331 | return |
332 | cr = pooler.get_db(db).cursor() |
333 | try: |
334 | - cr.execute('SELECT COUNT(1) FROM res_users WHERE id=%s AND password=%s AND active=%s', |
335 | - (int(uid), passwd, True)) |
336 | - res = cr.fetchone()[0] |
337 | - if not res: |
338 | + database_password = self.get_user_database_password_from_uid(cr, uid) |
339 | + # check the password is a bcrypt encrypted one |
340 | + database_password = tools.ustr(database_password) |
341 | + passwd = tools.ustr(passwd) |
342 | + if bcrypt.identify(database_password): |
343 | + if not bcrypt.verify(passwd, database_password): |
344 | + raise security.ExceptionNoTb('AccessDenied') |
345 | + elif passwd != database_password: |
346 | raise security.ExceptionNoTb('AccessDenied') |
347 | + |
348 | if self._uid_cache.has_key(db): |
349 | ulist = self._uid_cache[db] |
350 | ulist[uid] = passwd |
351 | @@ -542,55 +652,51 @@ |
352 | finally: |
353 | cr.close() |
354 | |
355 | - def is_password_strong(self, password, login): |
356 | - """ |
357 | - Check that given password is strong enough. |
358 | - In case it is, all values of the returned dict are True |
359 | - """ |
360 | - result = { |
361 | - 'has_digit': False, |
362 | - 'long_enough': False, |
363 | - 'login_not_equal_password': False, |
364 | - } |
365 | - |
366 | - # check it contains at least one digit |
367 | - if re.search(r'\d', password): |
368 | - result['has_digit'] = True |
369 | - |
370 | - # check password lenght |
371 | - if len(password) >= self.PASSWORD_MIN_LENGHT: |
372 | - result['long_enough'] = True |
373 | - |
374 | - # check login != password: |
375 | - if password != login: |
376 | - result['login_not_equal_password'] = True |
377 | - |
378 | - return result |
379 | - |
380 | - def change_password(self, cr, uid, old_passwd, new_passwd, context=None): |
381 | + def pref_change_password(self, cr, uid, old_passwd, new_passwd, |
382 | + confirm_passwd, context=None): |
383 | + self.check(cr.dbname, uid, tools.ustr(old_passwd)) |
384 | + login = self.read(cr, uid, uid, ['login'])['login'] |
385 | + return self.change_password(cr.dbname, login, old_passwd, new_passwd, |
386 | + confirm_passwd, context=context) |
387 | + |
388 | + def change_password(self, db_name, login, old_passwd, new_passwd, |
389 | + confirm_passwd, context=None): |
390 | """Change current user password. Old password must be provided explicitly |
391 | to prevent hijacking an existing user session, or for cases where the cleartext |
392 | password is not used to authenticate requests. |
393 | |
394 | - The write of the new password is done with uid=1 to prevent raise it |
395 | + The write of the new password is done with uid=1 to prevent raise if |
396 | the current logged user don't have permission on res_users. |
397 | |
398 | :return: True |
399 | :raise: security.ExceptionNoTb when old password is wrong |
400 | :raise: except_osv when new password is not set or empty |
401 | """ |
402 | - self.check(cr.dbname, uid, old_passwd) |
403 | if new_passwd: |
404 | - login = self.read(cr, uid, uid, ['login'])['login'] |
405 | - if not all(self.is_password_strong(new_passwd, login).values()): |
406 | - raise osv.except_osv(_('Operation Canceled'), _('The new password is not strong enough. '\ |
407 | - 'Password must be diffrent from the login, it must contain '\ |
408 | - 'at least one number and be at least %s characters.' % self.PASSWORD_MIN_LENGHT)) |
409 | - vals = { |
410 | - 'password': new_passwd, |
411 | - 'force_password_change': False, |
412 | - } |
413 | - return self.write(cr, 1, uid, vals) |
414 | + cr = pooler.get_db(db_name).cursor() |
415 | + try: |
416 | + # get user_uid |
417 | + cr.execute("""SELECT id from res_users |
418 | + WHERE login=%s AND active=%s""", |
419 | + (login, True)) |
420 | + res = cr.fetchone() |
421 | + uid = None |
422 | + if res: |
423 | + uid = res[0] |
424 | + if not uid: |
425 | + raise security.ExceptionNoTb('AccessDenied') |
426 | + security.check_password_validity(self, cr, uid, old_passwd, new_passwd, confirm_passwd, login) |
427 | + new_passwd = bcrypt.encrypt(tools.ustr(new_passwd)) |
428 | + vals = { |
429 | + 'password': new_passwd, |
430 | + 'force_password_change': False, |
431 | + } |
432 | + self.check(db_name, uid, tools.ustr(old_passwd)) |
433 | + result = self.write(cr, 1, uid, vals) |
434 | + cr.commit() |
435 | + finally: |
436 | + cr.close() |
437 | + return result |
438 | raise osv.except_osv(_('Warning!'), _("Setting empty passwords is not allowed for security reasons!")) |
439 | |
440 | def get_admin_profile(self, cr, uid, context=None): |
441 | |
442 | === modified file 'bin/addons/msf_profile/data/patches.xml' |
443 | --- bin/addons/msf_profile/data/patches.xml 2016-11-08 15:50:26 +0000 |
444 | +++ bin/addons/msf_profile/data/patches.xml 2016-11-21 09:47:14 +0000 |
445 | @@ -1,10 +1,17 @@ |
446 | <?xml version="1.0" encoding="utf-8" ?> |
447 | <openerp> |
448 | <data> |
449 | +<<<<<<< TREE |
450 | <record id="us_1482" model="patch.scripts"> |
451 | <field name="method">us_1482_fix_default_code_on_msf_lines</field> |
452 | </record> |
453 | |
454 | +======= |
455 | + <record id="us_1381" model="patch.scripts"> |
456 | + <field name="method">us_1381_encrypt_passwords</field> |
457 | + </record> |
458 | + |
459 | +>>>>>>> MERGE-SOURCE |
460 | <record id="us_1388" model="patch.scripts"> |
461 | <field name="method">us_1388_change_sequence_implementation</field> |
462 | </record> |
463 | |
464 | === modified file 'bin/addons/msf_profile/i18n/fr_MF.po' |
465 | --- bin/addons/msf_profile/i18n/fr_MF.po 2016-11-09 15:21:10 +0000 |
466 | +++ bin/addons/msf_profile/i18n/fr_MF.po 2016-11-21 09:47:14 +0000 |
467 | @@ -74686,6 +74686,7 @@ |
468 | " Number of columns is not equal to %s" |
469 | msgstr "\n" |
470 | " Le nombre de colonnes n'est pas égal à %s" |
471 | +<<<<<<< TREE |
472 | |
473 | #. module: msf_partner |
474 | #: constraint:res.partner:0 |
475 | @@ -75092,3 +75093,83 @@ |
476 | msgid "Attachment configuration" |
477 | msgstr "Configuration des pièces jointes" |
478 | |
479 | +======= |
480 | + |
481 | +#. module: base |
482 | +#: code:addons/base/res/res_user.py:550 |
483 | +#, python-format |
484 | +msgid "The new password is not strong enough. Password must contain at least one digit." |
485 | +msgstr "Le nouveau mot de passe n'est pas assez robuste. Il doit contenir au moins un chiffre." |
486 | + |
487 | +#. module: base |
488 | +#: code:addons/base/res/res_user.py:556 |
489 | +#, python-format |
490 | +msgid "The new password is not strong enough. Password must be at least %s characters long." |
491 | +msgstr "Le nouveau mot de passe n'est pas assez robuste. Il doit être d'au moins %s caractères." |
492 | + |
493 | +#. module: base |
494 | +#: code:addons/base/res/res_user.py:561 |
495 | +#, python-format |
496 | +msgid "The new password cannot be equal to the login." |
497 | +msgstr "Le nouveau mot de passe ne peut pas être égal à l'identifiant." |
498 | + |
499 | +#. module: base |
500 | +#: code:addons/base/res/res_user.py:566 |
501 | +#, python-format |
502 | +msgid "The new password does not match the confirm password." |
503 | +msgstr "Le nouveau mot de passe ne correspond pas à la confirmation." |
504 | + |
505 | +#. module: base |
506 | +#: code:addons/base/res/res_user.py:571 |
507 | +#, python-format |
508 | +msgid "The new password must be different from the actual one." |
509 | +msgstr "Le nouveau mot de passe doit être différent de l'actuel." |
510 | + |
511 | +#. module: base |
512 | +#: code:addons/base/res/res_user.py:537 |
513 | +#, python-format |
514 | +msgid "Bad username or password" |
515 | +msgstr "Mauvais nom d'utilisateur ou mot de passe" |
516 | + |
517 | +#. module: base |
518 | +#: code:addons/base/res/res_user.py:623 |
519 | +#, python-format |
520 | +msgid "Setting empty passwords is not allowed for security reasons!" |
521 | +msgstr "La définition de mot de passe vides n'est pas autorisée pour des raisons de sécurité !" |
522 | + |
523 | +#. module: base |
524 | +#: code:addons/base/res/res_user.py:229 |
525 | +#, python-format |
526 | +msgid "Change password" |
527 | +msgstr "Changer le mot de passe" |
528 | + |
529 | +#. module: base |
530 | +#: code:addons/base/res/res_user.py:226 |
531 | +#, python-format |
532 | +msgid "Password" |
533 | +msgstr "Mot de passe" |
534 | + |
535 | +#. module: base |
536 | +#: code:addons/base/res/res_user.py:224 |
537 | +#, python-format |
538 | +msgid "Login" |
539 | +msgstr "Identifiant" |
540 | + |
541 | +#. module: base |
542 | +#: code:addons/base/res/res_user.py:238 |
543 | +#, python-format |
544 | +msgid "Change password on next login" |
545 | +msgstr "Changer le mot de passe à la prochaine connexion" |
546 | + |
547 | +#. module: base |
548 | +#: code:addons/base/res/res_user.py:239 |
549 | +#, python-format |
550 | +msgid "Check out this box to force this user to change his password on next login." |
551 | +msgstr "Cocher cette case pour forcer l'utilisateur à changer son mot de passe à la prochaine connexion." |
552 | + |
553 | +#. module: base |
554 | +#: code:addons/base/res/res_user.py:216 |
555 | +#, python-format |
556 | +msgid "Please use the change password wizard (in User Preferences or User menu) to change your own password." |
557 | +msgstr "Merci d'utiliser l'assistant (dans les préférences de l'utilisateur ou le menu de l'utilisateur) pour changer votre propre mot de passe." |
558 | +>>>>>>> MERGE-SOURCE |
559 | |
560 | === modified file 'bin/addons/msf_profile/msf_profile.py' |
561 | --- bin/addons/msf_profile/msf_profile.py 2016-11-09 13:21:34 +0000 |
562 | +++ bin/addons/msf_profile/msf_profile.py 2016-11-21 09:47:14 +0000 |
563 | @@ -46,6 +46,7 @@ |
564 | 'model': lambda *a: 'patch.scripts', |
565 | } |
566 | |
567 | +<<<<<<< TREE |
568 | def us_1482_fix_default_code_on_msf_lines(self, cr, uid, *a, **b): |
569 | """ |
570 | If the default code set on the MSR lines is different from the |
571 | @@ -69,6 +70,23 @@ |
572 | """ |
573 | cr.execute(request) |
574 | |
575 | +======= |
576 | + def us_1381_encrypt_passwords(self, cr, uid, *a, **b): |
577 | + """ |
578 | + encrypt all passwords |
579 | + """ |
580 | + from passlib.hash import bcrypt |
581 | + users_obj = self.pool.get('res.users') |
582 | + user_ids = users_obj.search(cr, uid, []) |
583 | + for user in users_obj.read(cr, uid, user_ids, ['password']): |
584 | + original_password = tools.ustr(user['password']) |
585 | + # check the password is not already encrypted |
586 | + if not bcrypt.identify(original_password): |
587 | + encrypted_password = bcrypt.encrypt(original_password) |
588 | + users_obj.write(cr, uid, user['id'], |
589 | + {'password': encrypted_password}) |
590 | + |
591 | +>>>>>>> MERGE-SOURCE |
592 | def us_1388_change_sequence_implementation(self, cr, uid, *a, **b): |
593 | """ |
594 | change the implementation of the finance.ocb.export ir_sequence to be |
595 | |
596 | === modified file 'bin/addons/msf_profile/user_access_configurator_view.xml' |
597 | --- bin/addons/msf_profile/user_access_configurator_view.xml 2013-12-11 10:02:33 +0000 |
598 | +++ bin/addons/msf_profile/user_access_configurator_view.xml 2016-11-21 09:47:14 +0000 |
599 | @@ -395,6 +395,7 @@ |
600 | <field name="arch" type="xml"> |
601 | <search string="Users"> |
602 | <filter name="show_inactive" string="Show inactive" icon="gtk-ok" domain="[('active', '=', False)]" /> |
603 | + <filter name="show_synchronized" string="Show synchronized" icon="gtk-ok" domain="[('synchronize', '=', True)]"/> |
604 | <field name="name" /> |
605 | <field name="login" /> |
606 | <field name="address_id" string="Address" /> |
607 | |
608 | === modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv' |
609 | --- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2016-11-03 15:58:00 +0000 |
610 | +++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2016-11-21 09:47:14 +0000 |
611 | @@ -252,3 +252,4 @@ |
612 | msf_usb_sync_data_server.button_access_rules,FALSE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,[],"['active', 'comment', 'group_ids/id', 'group_names', 'label', 'model_id/id', 'name', 'type', 'view_id/id']",USB,msf_button_access_rights.button_access_rule,,[USER RIGHTS] Button Access Rules,Valid,,4405 |
613 | msf_usb_sync_data_server.button_access_rules2,TRUE,TRUE,FALSE,TRUE,cp_to_rw,Bidirectional,[],"['active', 'comment', 'group_ids/id', 'group_names', 'label', 'model_id/id', 'name', 'type', 'view_id/id', 'xmlname']",USB,msf_button_access_rights.button_access_rule,,[USER RIGHTS] Button Access Rules,Valid,,4415 |
614 | msf_sync_data_server.ir_translation_USB,TRUE,TRUE,TRUE,TRUE,cp_to_rw,Down,"[('res_id', '!=', 0), ('src', '!=', False), ('xml_id', '!=', False), ('lang', '!=', 'en_US'), ('type', '=', 'model'), ('name', 'in', ['res.partner.category,name','product.uom,name','product.template,description_purchase','product.template,description','product.template,description_sale','product.uom.categ,name','product.pricelist,name','product.category,name','product.nomenclature,name','product.product,form_value','product.product,function_value','product.product,fit_value','res.currency,currency_name','res.country,name','product.ul,name','product.price.type,name','product.pricelist.type,name','product.pricelist.version,name','account.analytic.account,name','account.fiscal.position,note','hr.employee.marital.status,name','account.period,name','account.tax.code,name','account.journal,name','account.tax,name','account.analytic.journal,name','account.account,name','stock.reason.type,name','product.template,name','product.justification.code,code'])]","['lang', 'src', 'name', 'value', 'type', 'xml_id']",USB,ir.translation,,[OTHER] Translations,Valid,,4406 |
615 | +msf_sync_data_server.res_users,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,"[('synchronize', '=', True)]","['action_id/id', 'active', 'address_id/id', 'email', 'force_password_change', 'groups_id/id', 'login', 'menu_id/id', 'menu_tips', 'name', 'password', 'signature', 'user_email', 'view']",OC,res.users,,Res Users,Valid,,1002 |
616 | |
617 | === modified file 'bin/addons/sync_client/rpc.py' |
618 | --- bin/addons/sync_client/rpc.py 2016-10-27 13:23:52 +0000 |
619 | +++ bin/addons/sync_client/rpc.py 2016-11-21 09:47:14 +0000 |
620 | @@ -373,7 +373,10 @@ |
621 | self.user_id = user_id |
622 | if user_id is None: |
623 | self.user_id = Common(self.connector).login(self.database, self.login, self.password) |
624 | +<<<<<<< TREE |
625 | |
626 | +======= |
627 | +>>>>>>> MERGE-SOURCE |
628 | if self.user_id is False: |
629 | raise osv.except_osv(_('Error!'), _('Unable to connect to the distant server with this user!')) |
630 | self._logger.debug(self.user_id) |
631 | |
632 | === modified file 'bin/openerp-server.py' |
633 | --- bin/openerp-server.py 2016-08-03 09:59:23 +0000 |
634 | +++ bin/openerp-server.py 2016-11-21 09:47:14 +0000 |
635 | @@ -125,15 +125,14 @@ |
636 | for dbname in tools.config['db_name'].split(','): |
637 | db,pool = pooler.get_db_and_pool(dbname, update_module=tools.config['init'] or tools.config['update'], pooljobs=False) |
638 | cr = db.cursor() |
639 | - |
640 | - if tools.config["test_file"]: |
641 | - logger.info('loading test file %s', tools.config["test_file"]) |
642 | - tools.convert_yaml_import(cr, 'base', file(tools.config["test_file"]), {}, 'test', True) |
643 | - cr.rollback() |
644 | - |
645 | - pool.get('ir.cron')._poolJobs(db.dbname) |
646 | - |
647 | - cr.close() |
648 | + try: |
649 | + if tools.config["test_file"]: |
650 | + logger.info('loading test file %s', tools.config["test_file"]) |
651 | + tools.convert_yaml_import(cr, 'base', file(tools.config["test_file"]), {}, 'test', True) |
652 | + cr.rollback() |
653 | + pool.get('ir.cron')._poolJobs(db.dbname) |
654 | + finally: |
655 | + cr.close() |
656 | |
657 | #---------------------------------------------------------- |
658 | # translation stuff |
659 | @@ -151,9 +150,11 @@ |
660 | buf = file(tools.config["translate_out"], "w") |
661 | dbname = tools.config['db_name'] |
662 | cr = pooler.get_db(dbname).cursor() |
663 | - tools.trans_export(tools.config["language"], tools.config["translate_modules"] or ["all"], buf, fileformat, cr) |
664 | - cr.close() |
665 | - buf.close() |
666 | + try: |
667 | + tools.trans_export(tools.config["language"], tools.config["translate_modules"] or ["all"], buf, fileformat, cr) |
668 | + finally: |
669 | + cr.close() |
670 | + buf.close() |
671 | |
672 | logger.info('translation file written successfully') |
673 | sys.exit(0) |
674 | @@ -162,13 +163,15 @@ |
675 | context = {'overwrite': tools.config["overwrite_existing_translations"]} |
676 | dbname = tools.config['db_name'] |
677 | cr = pooler.get_db(dbname).cursor() |
678 | - tools.trans_load(cr, |
679 | - tools.config["translate_in"], |
680 | - tools.config["language"], |
681 | - context=context) |
682 | - tools.trans_update_res_ids(cr) |
683 | - cr.commit() |
684 | - cr.close() |
685 | + try: |
686 | + tools.trans_load(cr, |
687 | + tools.config["translate_in"], |
688 | + tools.config["language"], |
689 | + context=context) |
690 | + tools.trans_update_res_ids(cr) |
691 | + finally: |
692 | + cr.commit() |
693 | + cr.close() |
694 | sys.exit(0) |
695 | |
696 | #---------------------------------------------------------------------------------- |
697 | |
698 | === modified file 'bin/service/security.py' |
699 | --- bin/service/security.py 2016-07-19 13:25:46 +0000 |
700 | +++ bin/service/security.py 2016-11-21 09:47:14 +0000 |
701 | @@ -23,6 +23,11 @@ |
702 | import tools |
703 | import threading |
704 | import updater |
705 | +import re |
706 | +from passlib.hash import bcrypt |
707 | +from tools.translate import _ |
708 | +from osv import osv |
709 | +PASSWORD_MIN_LENGHT = 6 |
710 | |
711 | # When rejecting a password, hide the traceback |
712 | class ExceptionNoTb(Exception): |
713 | @@ -32,8 +37,10 @@ |
714 | |
715 | def number_update_modules(db): |
716 | cr = pooler.get_db_only(db).cursor() |
717 | - n = _get_number_modules(cr) |
718 | - cr.close() |
719 | + try: |
720 | + n = _get_number_modules(cr) |
721 | + finally: |
722 | + cr.close() |
723 | return n |
724 | |
725 | def _get_number_modules(cr, testlogin=False): |
726 | @@ -51,57 +58,145 @@ |
727 | return True |
728 | return False |
729 | |
730 | -def login(db, login, password): |
731 | - cr = pooler.get_db_only(db).cursor() |
732 | - nb = _get_number_modules(cr, testlogin=True) |
733 | - patch_failed = [0] |
734 | - cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname='patch_scripts'") |
735 | - if cr.rowcount: |
736 | - cr.execute("SELECT count(id) FROM patch_scripts WHERE run = \'f\'") |
737 | - patch_failed = cr.fetchone() |
738 | - to_update = False |
739 | - if not nb: |
740 | - to_update = updater.test_do_upgrade(cr) |
741 | - cr.close() |
742 | - if nb or to_update: |
743 | - s = threading.Thread(target=pooler.get_pool, args=(db,), |
744 | - kwargs={'threaded': True}) |
745 | - s.start() |
746 | - raise Exception("ServerUpdate: Server is updating modules ...") |
747 | - |
748 | - |
749 | - pool = pooler.get_pool(db) |
750 | +def change_password(db_name, login, password, new_password, confirm_password): |
751 | + ''' |
752 | + Call the res.user change_password method |
753 | + ''' |
754 | + db, pool = pooler.get_db_and_pool(db_name) |
755 | + cr = db.cursor() |
756 | + try: |
757 | + user_obj = pool.get('res.users') |
758 | + result = user_obj.change_password(db_name, login, password, new_password, |
759 | + confirm_password) |
760 | + finally: |
761 | + cr.close() |
762 | + return result |
763 | + |
764 | +def login(db_name, login, password): |
765 | + # it is required here not to get pool but only the db, if the pool it also |
766 | + # get, then the server will update the module at this step without display |
767 | + # the "Server is updating modules ..." message |
768 | + cr = pooler.get_db_only(db_name).cursor() |
769 | + try: |
770 | + nb = _get_number_modules(cr, testlogin=True) |
771 | + patch_failed = [0] |
772 | + cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname='patch_scripts'") |
773 | + if cr.rowcount: |
774 | + cr.execute("SELECT count(id) FROM patch_scripts WHERE run = \'f\'") |
775 | + patch_failed = cr.fetchone() |
776 | + to_update = False |
777 | + if not nb: |
778 | + to_update = updater.test_do_upgrade(cr) |
779 | + if nb or to_update: |
780 | + s = threading.Thread(target=pooler.get_pool, args=(db_name,), |
781 | + kwargs={'threaded': True}) |
782 | + s.start() |
783 | + raise Exception("ServerUpdate: Server is updating modules ...") |
784 | + |
785 | + pool = pooler.get_pool(db_name) |
786 | + # check if the user have to change his password |
787 | + cr.execute("""SELECT force_password_change |
788 | + FROM res_users |
789 | + WHERE login='%s'""" % (login)) |
790 | + force_password = [x[0] for x in cr.fetchall()] |
791 | + if any(force_password): |
792 | + raise Exception("ForcePasswordChange: The admin requests your password change ...") |
793 | + finally: |
794 | + cr.close() |
795 | + |
796 | user_obj = pool.get('res.users') |
797 | - user_res = user_obj.login(db, login, password) |
798 | + user_res = user_obj.login(db_name, login, password) |
799 | |
800 | if user_res != 1 and patch_failed[0]: |
801 | raise Exception("PatchFailed: A script during upgrade has failed. Login is forbidden. Please contact your administrator") |
802 | |
803 | return user_res |
804 | |
805 | +def check_password_validity(self, cr, uid, old_password, new_password, confirm_password, login): |
806 | + ''' |
807 | + Check password respect some conditions |
808 | + Raise is any of the condition is not respected |
809 | + |
810 | + :param self: the caller of this method. It is needed for _get_lang to be |
811 | + able to know the language of the requested user |
812 | + :param cr: database cursor |
813 | + :param uid: the user id of the password to check |
814 | + :param old_password: the previous password |
815 | + :param new_password: the new password to setup |
816 | + :param confirm_password: the confirmation of the new password |
817 | + :param login: the login that request the password check |
818 | + :return: True if the password check pass |
819 | + :rtype: boolean |
820 | + :raise osv.except_osv: if the password is not ok |
821 | + ''' |
822 | + # check it contains at least one digit |
823 | + if not re.search(r'\d', new_password): |
824 | + message = _('The new password is not strong enough. '\ |
825 | + 'Password must contain at least one digit.') |
826 | + raise osv.except_osv(_('Operation Canceled'), message) |
827 | + |
828 | + # check new_password lenght |
829 | + if len(new_password) < PASSWORD_MIN_LENGHT: |
830 | + message = _('The new password is not strong enough. '\ |
831 | + 'Password must be at least %s characters long.' % PASSWORD_MIN_LENGHT) |
832 | + raise osv.except_osv(_('Operation Canceled'), message) |
833 | + |
834 | + # check login != new_password: |
835 | + if new_password == login: |
836 | + message = _('The new password cannot be equal to the login.') |
837 | + raise osv.except_osv(_('Operation Canceled'), message) |
838 | + |
839 | + # check confirm_password == new_password: |
840 | + if new_password != confirm_password: |
841 | + message = _('The new password does not match the confirm password.') |
842 | + raise osv.except_osv(_('Operation Canceled'), message) |
843 | + |
844 | + # check new_password != old_password |
845 | + if new_password == old_password: |
846 | + message = _('The new password must be different from the actual one.') |
847 | + raise osv.except_osv(_('Operation Canceled'), message) |
848 | + return True |
849 | + |
850 | +def check_password(password, hashed_password): |
851 | + ''' |
852 | + Check that the password match the hashed_password |
853 | + |
854 | + :param password: a string containing the password to check |
855 | + :param hashed_password: a string containing the hash to check against, such |
856 | + as returned by encrypt() |
857 | + :return: True if the password match |
858 | + :rtype: boolean |
859 | + :raise ExceptionNoTb: if password don't match |
860 | + ''' |
861 | + hashed_password = tools.ustr(hashed_password) |
862 | + password = tools.ustr(password) |
863 | + |
864 | + # check the password is a bcrypt encrypted one |
865 | + if bcrypt.identify(hashed_password) and \ |
866 | + bcrypt.verify(password, hashed_password): |
867 | + return True |
868 | + elif password == hashed_password: |
869 | + # this is a not encrypted password (we want to keep compatibility with |
870 | + # old way password |
871 | + return True |
872 | + else: |
873 | + raise ExceptionNoTb('AccessDenied: Invalid super administrator password.') |
874 | + |
875 | +def check_super_password_validity(password): |
876 | + check_password_validity(None, None, 1, None, password, password, 'admin') |
877 | + return True |
878 | + |
879 | def check_super(passwd): |
880 | - if passwd == tools.config['admin_passwd']: |
881 | - return True |
882 | - else: |
883 | - raise ExceptionNoTb('AccessDenied: Invalid super administrator password.') |
884 | + return check_password(passwd, tools.config['admin_passwd']) |
885 | |
886 | def check_super_dropdb(passwd): |
887 | - if passwd == tools.config['admin_dropdb_passwd']: |
888 | - return True |
889 | - else: |
890 | - raise ExceptionNoTb('AccessDenied: Invalid super administrator password.') |
891 | + return check_password(passwd, tools.config['admin_dropdb_passwd']) |
892 | |
893 | def check_super_bkpdb(passwd): |
894 | - if passwd == tools.config['admin_bkpdb_passwd']: |
895 | - return True |
896 | - else: |
897 | - raise ExceptionNoTb('AccessDenied: Invalid super administrator password.') |
898 | + return check_password(passwd, tools.config['admin_bkpdb_passwd']) |
899 | |
900 | def check_super_restoredb(passwd): |
901 | - if passwd == tools.config['admin_restoredb_passwd']: |
902 | - return True |
903 | - else: |
904 | - raise ExceptionNoTb('AccessDenied: Invalid super administrator password.') |
905 | + return check_password(passwd, tools.config['admin_restoredb_passwd']) |
906 | |
907 | def check(db, uid, passwd): |
908 | pool = pooler.get_pool(db) |
909 | |
910 | === modified file 'bin/service/web_services.py' |
911 | --- bin/service/web_services.py 2016-11-09 09:47:06 +0000 |
912 | +++ bin/service/web_services.py 2016-11-21 09:47:14 +0000 |
913 | @@ -44,11 +44,15 @@ |
914 | from cStringIO import StringIO |
915 | from tempfile import NamedTemporaryFile |
916 | from updater import get_server_version |
917 | +<<<<<<< TREE |
918 | from tools.misc import file_open |
919 | from mako.template import Template |
920 | from mako import exceptions |
921 | from mako.runtime import Context |
922 | import codecs |
923 | +======= |
924 | +from passlib.hash import bcrypt |
925 | +>>>>>>> MERGE-SOURCE |
926 | |
927 | def export_csv(fields, result, result_file_path): |
928 | try: |
929 | @@ -111,7 +115,12 @@ |
930 | params = params[1:] |
931 | security.check_super(passwd) |
932 | elif method in [ 'db_exist', 'list', 'list_lang', 'server_version', |
933 | +<<<<<<< TREE |
934 | 'check_timezone', 'connected_to_prod_sync_server' ]: |
935 | +======= |
936 | + 'check_timezone', 'connected_to_prod_sync_server', |
937 | + 'check_super_password_validity' ]: |
938 | +>>>>>>> MERGE-SOURCE |
939 | # params = params |
940 | # No security check for these methods |
941 | pass |
942 | @@ -137,11 +146,12 @@ |
943 | self.id += 1 |
944 | id = self.id |
945 | self.id_protect.release() |
946 | - |
947 | self.actions[id] = {'clean': False} |
948 | - |
949 | self._create_empty_database(db_name) |
950 | |
951 | + # encrypt the db admin password |
952 | + user_password = bcrypt.encrypt(tools.ustr(user_password)) |
953 | + |
954 | class DBInitialize(object): |
955 | def __call__(self, serv, id, db_name, demo, lang, user_password='admin'): |
956 | cr = None |
957 | @@ -171,7 +181,6 @@ |
958 | serv.actions[id]['users'] = cr.dictfetchall() |
959 | serv.actions[id]['clean'] = True |
960 | cr.commit() |
961 | - cr.close(True) |
962 | except Exception, e: |
963 | serv.actions[id]['clean'] = False |
964 | serv.actions[id]['exception'] = e |
965 | @@ -182,6 +191,7 @@ |
966 | e_str.close() |
967 | netsvc.Logger().notifyChannel('web-services', netsvc.LOG_ERROR, 'CREATE DATABASE\n%s' % (traceback_str)) |
968 | serv.actions[id]['traceback'] = traceback_str |
969 | + finally: |
970 | if cr: |
971 | cr.close(True) |
972 | logger = netsvc.Logger() |
973 | @@ -193,6 +203,13 @@ |
974 | self.actions[id]['thread'] = create_thread |
975 | return id |
976 | |
977 | + def exp_check_super_password_validity(self, password): |
978 | + try: |
979 | + security.check_super_password_validity(password) |
980 | + except Exception as e: |
981 | + return str(e) |
982 | + return True |
983 | + |
984 | def exp_check_timezone(self): |
985 | return check_tz() |
986 | |
987 | @@ -380,10 +397,12 @@ |
988 | return False |
989 | |
990 | cr = connection.cursor() |
991 | - cr.execute('''SELECT host, database |
992 | - FROM sync_client_sync_server_connection''') |
993 | - host, database = cr.fetchone() |
994 | - cr.close() |
995 | + try: |
996 | + cr.execute('''SELECT host, database |
997 | + FROM sync_client_sync_server_connection''') |
998 | + host, database = cr.fetchone() |
999 | + finally: |
1000 | + cr.close() |
1001 | if host and database and database.strip() == 'SYNC_SERVER' and \ |
1002 | ('sync.unifield.net' in host.lower() or '212.95.73.129' in host): |
1003 | return True |
1004 | @@ -409,16 +428,18 @@ |
1005 | db = sql_db.db_connect(dbname) |
1006 | cr = db.cursor() |
1007 | |
1008 | - # check sync_client_version table existance |
1009 | - cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname='sync_client_version'") |
1010 | - if not cr.fetchone(): |
1011 | - # the table sync_client_version doesn't exists, fallback on the |
1012 | - # version from release.py file |
1013 | - return release.version or 'UNKNOWN_VERSION' |
1014 | + try: |
1015 | + # check sync_client_version table existance |
1016 | + cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname='sync_client_version'") |
1017 | + if not cr.fetchone(): |
1018 | + # the table sync_client_version doesn't exists, fallback on the |
1019 | + # version from release.py file |
1020 | + return release.version or 'UNKNOWN_VERSION' |
1021 | |
1022 | - cr.execute("SELECT name, sum FROM sync_client_version WHERE state='installed' ORDER BY applied DESC") |
1023 | - res = cr.fetchone() |
1024 | - cr.close(True) |
1025 | + cr.execute("SELECT name, sum FROM sync_client_version WHERE state='installed' ORDER BY applied DESC") |
1026 | + res = cr.fetchone() |
1027 | + finally: |
1028 | + cr.close(True) |
1029 | if res and res[0]: |
1030 | return res[0] |
1031 | elif res[1]: |
1032 | @@ -459,10 +480,12 @@ |
1033 | params = params[3:] |
1034 | security.check(db,uid,passwd) |
1035 | cr = pooler.get_db(db).cursor() |
1036 | - fn = getattr(self, 'exp_'+method) |
1037 | - res = fn(cr, uid, *params) |
1038 | - cr.commit() |
1039 | - cr.close() |
1040 | + try: |
1041 | + fn = getattr(self, 'exp_'+method) |
1042 | + res = fn(cr, uid, *params) |
1043 | + cr.commit() |
1044 | + finally: |
1045 | + cr.close() |
1046 | return res |
1047 | |
1048 | class common(_ObjectService): |
1049 | @@ -484,6 +507,17 @@ |
1050 | return res or False |
1051 | elif method == 'number_update_modules': |
1052 | return security.number_update_modules(params[0]) |
1053 | + elif method == 'change_password': |
1054 | + try: |
1055 | + security.change_password(params[0], params[1], params[2], |
1056 | + params[3], params[4]) |
1057 | + except Exception as e: |
1058 | + if hasattr(e, 'value'): |
1059 | + msg = tools.ustr(e.value) |
1060 | + else: |
1061 | + msg = tools.ustr(e) |
1062 | + return msg |
1063 | + return True |
1064 | elif method == 'logout': |
1065 | if auth: |
1066 | auth.logout(params[1]) |
1067 | @@ -1009,8 +1043,9 @@ |
1068 | else: |
1069 | self._reports[id]['exception'] = ExceptionWithTraceback(tools.exception_to_unicode(exception), tb) |
1070 | self._reports[id]['state'] = True |
1071 | - cr.commit() |
1072 | - cr.close() |
1073 | + finally: |
1074 | + cr.commit() |
1075 | + cr.close() |
1076 | return True |
1077 | |
1078 | thread.start_new_thread(go, (id, uid, ids, datas, context)) |
1079 | |
1080 | === modified file 'setup.py' |
1081 | --- setup.py 2016-11-09 13:21:34 +0000 |
1082 | +++ setup.py 2016-11-21 09:47:14 +0000 |
1083 | @@ -65,7 +65,11 @@ |
1084 | "HTMLParser", "select", "mako", "poplib", |
1085 | "imaplib", "smtplib", "email", "yaml", "DAV", |
1086 | "uuid", "commands", "mx.DateTime", "json", |
1087 | +<<<<<<< TREE |
1088 | "pylzma", "xlwt" |
1089 | +======= |
1090 | + "pylzma", "passlib", "bcrypt", "six", "cffi", |
1091 | +>>>>>>> MERGE-SOURCE |
1092 | ], |
1093 | "excludes" : ["Tkconstants","Tkinter","tcl"], |
1094 | } |