##// END OF EJS Templates
fix crypt password on update my account
marcink -
r2488:b5b34d71 beta
parent child Browse files
Show More
@@ -1,616 +1,617
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons import url
30 30 from pylons.i18n.translation import _
31 31
32 32 from sqlalchemy.exc import DatabaseError
33 33 from sqlalchemy.orm import joinedload
34 34
35 35 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
36 36 from rhodecode.lib.caching_query import FromCache
37 37 from rhodecode.model import BaseModel
38 38 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
39 39 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
40 40 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
41 41 UserEmailMap
42 42 from rhodecode.lib.exceptions import DefaultUserException, \
43 43 UserOwnsReposException
44 44
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 PERM_WEIGHTS = {
50 50 'repository.none': 0,
51 51 'repository.read': 1,
52 52 'repository.write': 3,
53 53 'repository.admin': 4,
54 54 'group.none': 0,
55 55 'group.read': 1,
56 56 'group.write': 3,
57 57 'group.admin': 4,
58 58 }
59 59
60 60
61 61 class UserModel(BaseModel):
62 62
63 63 def get(self, user_id, cache=False):
64 64 user = self.sa.query(User)
65 65 if cache:
66 66 user = user.options(FromCache("sql_cache_short",
67 67 "get_user_%s" % user_id))
68 68 return user.get(user_id)
69 69
70 70 def get_user(self, user):
71 71 return self._get_user(user)
72 72
73 73 def get_by_username(self, username, cache=False, case_insensitive=False):
74 74
75 75 if case_insensitive:
76 76 user = self.sa.query(User).filter(User.username.ilike(username))
77 77 else:
78 78 user = self.sa.query(User)\
79 79 .filter(User.username == username)
80 80 if cache:
81 81 user = user.options(FromCache("sql_cache_short",
82 82 "get_user_%s" % username))
83 83 return user.scalar()
84 84
85 85 def get_by_api_key(self, api_key, cache=False):
86 86 return User.get_by_api_key(api_key, cache)
87 87
88 88 def create(self, form_data):
89 89 from rhodecode.lib.auth import get_crypt_password
90 90 try:
91 91 new_user = User()
92 92 for k, v in form_data.items():
93 93 if k == 'password':
94 94 v = get_crypt_password(v)
95 95 setattr(new_user, k, v)
96 96
97 97 new_user.api_key = generate_api_key(form_data['username'])
98 98 self.sa.add(new_user)
99 99 return new_user
100 100 except:
101 101 log.error(traceback.format_exc())
102 102 raise
103 103
104 104 def create_or_update(self, username, password, email, name, lastname,
105 105 active=True, admin=False, ldap_dn=None):
106 106 """
107 107 Creates a new instance if not found, or updates current one
108 108
109 109 :param username:
110 110 :param password:
111 111 :param email:
112 112 :param active:
113 113 :param name:
114 114 :param lastname:
115 115 :param active:
116 116 :param admin:
117 117 :param ldap_dn:
118 118 """
119 119
120 120 from rhodecode.lib.auth import get_crypt_password
121 121
122 122 log.debug('Checking for %s account in RhodeCode database' % username)
123 123 user = User.get_by_username(username, case_insensitive=True)
124 124 if user is None:
125 125 log.debug('creating new user %s' % username)
126 126 new_user = User()
127 127 else:
128 128 log.debug('updating user %s' % username)
129 129 new_user = user
130 130
131 131 try:
132 132 new_user.username = username
133 133 new_user.admin = admin
134 134 new_user.password = get_crypt_password(password)
135 135 new_user.api_key = generate_api_key(username)
136 136 new_user.email = email
137 137 new_user.active = active
138 138 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
139 139 new_user.name = name
140 140 new_user.lastname = lastname
141 141 self.sa.add(new_user)
142 142 return new_user
143 143 except (DatabaseError,):
144 144 log.error(traceback.format_exc())
145 145 raise
146 146
147 147 def create_for_container_auth(self, username, attrs):
148 148 """
149 149 Creates the given user if it's not already in the database
150 150
151 151 :param username:
152 152 :param attrs:
153 153 """
154 154 if self.get_by_username(username, case_insensitive=True) is None:
155 155
156 156 # autogenerate email for container account without one
157 157 generate_email = lambda usr: '%s@container_auth.account' % usr
158 158
159 159 try:
160 160 new_user = User()
161 161 new_user.username = username
162 162 new_user.password = None
163 163 new_user.api_key = generate_api_key(username)
164 164 new_user.email = attrs['email']
165 165 new_user.active = attrs.get('active', True)
166 166 new_user.name = attrs['name'] or generate_email(username)
167 167 new_user.lastname = attrs['lastname']
168 168
169 169 self.sa.add(new_user)
170 170 return new_user
171 171 except (DatabaseError,):
172 172 log.error(traceback.format_exc())
173 173 self.sa.rollback()
174 174 raise
175 175 log.debug('User %s already exists. Skipping creation of account'
176 176 ' for container auth.', username)
177 177 return None
178 178
179 179 def create_ldap(self, username, password, user_dn, attrs):
180 180 """
181 181 Checks if user is in database, if not creates this user marked
182 182 as ldap user
183 183
184 184 :param username:
185 185 :param password:
186 186 :param user_dn:
187 187 :param attrs:
188 188 """
189 189 from rhodecode.lib.auth import get_crypt_password
190 190 log.debug('Checking for such ldap account in RhodeCode database')
191 191 if self.get_by_username(username, case_insensitive=True) is None:
192 192
193 193 # autogenerate email for ldap account without one
194 194 generate_email = lambda usr: '%s@ldap.account' % usr
195 195
196 196 try:
197 197 new_user = User()
198 198 username = username.lower()
199 199 # add ldap account always lowercase
200 200 new_user.username = username
201 201 new_user.password = get_crypt_password(password)
202 202 new_user.api_key = generate_api_key(username)
203 203 new_user.email = attrs['email'] or generate_email(username)
204 204 new_user.active = attrs.get('active', True)
205 205 new_user.ldap_dn = safe_unicode(user_dn)
206 206 new_user.name = attrs['name']
207 207 new_user.lastname = attrs['lastname']
208 208
209 209 self.sa.add(new_user)
210 210 return new_user
211 211 except (DatabaseError,):
212 212 log.error(traceback.format_exc())
213 213 self.sa.rollback()
214 214 raise
215 215 log.debug('this %s user exists skipping creation of ldap account',
216 216 username)
217 217 return None
218 218
219 219 def create_registration(self, form_data):
220 220 from rhodecode.model.notification import NotificationModel
221 221
222 222 try:
223 223 form_data['admin'] = False
224 224 new_user = self.create(form_data)
225 225
226 226 self.sa.add(new_user)
227 227 self.sa.flush()
228 228
229 229 # notification to admins
230 230 subject = _('new user registration')
231 231 body = ('New user registration\n'
232 232 '---------------------\n'
233 233 '- Username: %s\n'
234 234 '- Full Name: %s\n'
235 235 '- Email: %s\n')
236 236 body = body % (new_user.username, new_user.full_name,
237 237 new_user.email)
238 238 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
239 239 kw = {'registered_user_url': edit_url}
240 240 NotificationModel().create(created_by=new_user, subject=subject,
241 241 body=body, recipients=None,
242 242 type_=Notification.TYPE_REGISTRATION,
243 243 email_kwargs=kw)
244 244
245 245 except:
246 246 log.error(traceback.format_exc())
247 247 raise
248 248
249 249 def update(self, user_id, form_data):
250 from rhodecode.lib.auth import get_crypt_password
250 251 try:
251 252 user = self.get(user_id, cache=False)
252 253 if user.username == 'default':
253 254 raise DefaultUserException(
254 255 _("You can't Edit this user since it's"
255 256 " crucial for entire application"))
256 257
257 258 for k, v in form_data.items():
258 259 if k == 'new_password' and v != '':
259 user.password = v
260 user.password = get_crypt_password(v)
260 261 user.api_key = generate_api_key(user.username)
261 262 else:
262 263 setattr(user, k, v)
263 264
264 265 self.sa.add(user)
265 266 except:
266 267 log.error(traceback.format_exc())
267 268 raise
268 269
269 270 def update_my_account(self, user_id, form_data):
270 271 from rhodecode.lib.auth import get_crypt_password
271 272 try:
272 273 user = self.get(user_id, cache=False)
273 274 if user.username == 'default':
274 275 raise DefaultUserException(
275 276 _("You can't Edit this user since it's"
276 277 " crucial for entire application")
277 278 )
278 279 for k, v in form_data.items():
279 280 if k == 'new_password' and v != '':
280 281 user.password = get_crypt_password(v)
281 282 user.api_key = generate_api_key(user.username)
282 283 else:
283 284 if k not in ['admin', 'active']:
284 285 setattr(user, k, v)
285 286
286 287 self.sa.add(user)
287 288 except:
288 289 log.error(traceback.format_exc())
289 290 raise
290 291
291 292 def delete(self, user):
292 293 user = self._get_user(user)
293 294
294 295 try:
295 296 if user.username == 'default':
296 297 raise DefaultUserException(
297 298 _(u"You can't remove this user since it's"
298 299 " crucial for entire application")
299 300 )
300 301 if user.repositories:
301 302 repos = [x.repo_name for x in user.repositories]
302 303 raise UserOwnsReposException(
303 304 _(u'user "%s" still owns %s repositories and cannot be '
304 305 'removed. Switch owners or remove those repositories. %s')
305 306 % (user.username, len(repos), ', '.join(repos))
306 307 )
307 308 self.sa.delete(user)
308 309 except:
309 310 log.error(traceback.format_exc())
310 311 raise
311 312
312 313 def reset_password_link(self, data):
313 314 from rhodecode.lib.celerylib import tasks, run_task
314 315 run_task(tasks.send_password_link, data['email'])
315 316
316 317 def reset_password(self, data):
317 318 from rhodecode.lib.celerylib import tasks, run_task
318 319 run_task(tasks.reset_user_password, data['email'])
319 320
320 321 def fill_data(self, auth_user, user_id=None, api_key=None):
321 322 """
322 323 Fetches auth_user by user_id,or api_key if present.
323 324 Fills auth_user attributes with those taken from database.
324 325 Additionally set's is_authenitated if lookup fails
325 326 present in database
326 327
327 328 :param auth_user: instance of user to set attributes
328 329 :param user_id: user id to fetch by
329 330 :param api_key: api key to fetch by
330 331 """
331 332 if user_id is None and api_key is None:
332 333 raise Exception('You need to pass user_id or api_key')
333 334
334 335 try:
335 336 if api_key:
336 337 dbuser = self.get_by_api_key(api_key)
337 338 else:
338 339 dbuser = self.get(user_id)
339 340
340 341 if dbuser is not None and dbuser.active:
341 342 log.debug('filling %s data' % dbuser)
342 343 for k, v in dbuser.get_dict().items():
343 344 setattr(auth_user, k, v)
344 345 else:
345 346 return False
346 347
347 348 except:
348 349 log.error(traceback.format_exc())
349 350 auth_user.is_authenticated = False
350 351 return False
351 352
352 353 return True
353 354
354 355 def fill_perms(self, user):
355 356 """
356 357 Fills user permission attribute with permissions taken from database
357 358 works for permissions given for repositories, and for permissions that
358 359 are granted to groups
359 360
360 361 :param user: user instance to fill his perms
361 362 """
362 363 RK = 'repositories'
363 364 GK = 'repositories_groups'
364 365 GLOBAL = 'global'
365 366 user.permissions[RK] = {}
366 367 user.permissions[GK] = {}
367 368 user.permissions[GLOBAL] = set()
368 369
369 370 #======================================================================
370 371 # fetch default permissions
371 372 #======================================================================
372 373 default_user = User.get_by_username('default', cache=True)
373 374 default_user_id = default_user.user_id
374 375
375 376 default_repo_perms = Permission.get_default_perms(default_user_id)
376 377 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
377 378
378 379 if user.is_admin:
379 380 #==================================================================
380 381 # admin user have all default rights for repositories
381 382 # and groups set to admin
382 383 #==================================================================
383 384 user.permissions[GLOBAL].add('hg.admin')
384 385
385 386 # repositories
386 387 for perm in default_repo_perms:
387 388 r_k = perm.UserRepoToPerm.repository.repo_name
388 389 p = 'repository.admin'
389 390 user.permissions[RK][r_k] = p
390 391
391 392 # repositories groups
392 393 for perm in default_repo_groups_perms:
393 394 rg_k = perm.UserRepoGroupToPerm.group.group_name
394 395 p = 'group.admin'
395 396 user.permissions[GK][rg_k] = p
396 397 return user
397 398
398 399 #==================================================================
399 400 # set default permissions first for repositories and groups
400 401 #==================================================================
401 402 uid = user.user_id
402 403
403 404 # default global permissions
404 405 default_global_perms = self.sa.query(UserToPerm)\
405 406 .filter(UserToPerm.user_id == default_user_id)
406 407
407 408 for perm in default_global_perms:
408 409 user.permissions[GLOBAL].add(perm.permission.permission_name)
409 410
410 411 # defaults for repositories, taken from default user
411 412 for perm in default_repo_perms:
412 413 r_k = perm.UserRepoToPerm.repository.repo_name
413 414 if perm.Repository.private and not (perm.Repository.user_id == uid):
414 415 # disable defaults for private repos,
415 416 p = 'repository.none'
416 417 elif perm.Repository.user_id == uid:
417 418 # set admin if owner
418 419 p = 'repository.admin'
419 420 else:
420 421 p = perm.Permission.permission_name
421 422
422 423 user.permissions[RK][r_k] = p
423 424
424 425 # defaults for repositories groups taken from default user permission
425 426 # on given group
426 427 for perm in default_repo_groups_perms:
427 428 rg_k = perm.UserRepoGroupToPerm.group.group_name
428 429 p = perm.Permission.permission_name
429 430 user.permissions[GK][rg_k] = p
430 431
431 432 #==================================================================
432 433 # overwrite defaults with user permissions if any found
433 434 #==================================================================
434 435
435 436 # user global permissions
436 437 user_perms = self.sa.query(UserToPerm)\
437 438 .options(joinedload(UserToPerm.permission))\
438 439 .filter(UserToPerm.user_id == uid).all()
439 440
440 441 for perm in user_perms:
441 442 user.permissions[GLOBAL].add(perm.permission.permission_name)
442 443
443 444 # user explicit permissions for repositories
444 445 user_repo_perms = \
445 446 self.sa.query(UserRepoToPerm, Permission, Repository)\
446 447 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
447 448 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
448 449 .filter(UserRepoToPerm.user_id == uid)\
449 450 .all()
450 451
451 452 for perm in user_repo_perms:
452 453 # set admin if owner
453 454 r_k = perm.UserRepoToPerm.repository.repo_name
454 455 if perm.Repository.user_id == uid:
455 456 p = 'repository.admin'
456 457 else:
457 458 p = perm.Permission.permission_name
458 459 user.permissions[RK][r_k] = p
459 460
460 461 # USER GROUP
461 462 #==================================================================
462 463 # check if user is part of user groups for this repository and
463 464 # fill in (or replace with higher) permissions
464 465 #==================================================================
465 466
466 467 # users group global
467 468 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
468 469 .options(joinedload(UsersGroupToPerm.permission))\
469 470 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
470 471 UsersGroupMember.users_group_id))\
471 472 .filter(UsersGroupMember.user_id == uid).all()
472 473
473 474 for perm in user_perms_from_users_groups:
474 475 user.permissions[GLOBAL].add(perm.permission.permission_name)
475 476
476 477 # users group for repositories permissions
477 478 user_repo_perms_from_users_groups = \
478 479 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
479 480 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
480 481 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
481 482 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
482 483 .filter(UsersGroupMember.user_id == uid)\
483 484 .all()
484 485
485 486 for perm in user_repo_perms_from_users_groups:
486 487 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
487 488 p = perm.Permission.permission_name
488 489 cur_perm = user.permissions[RK][r_k]
489 490 # overwrite permission only if it's greater than permission
490 491 # given from other sources
491 492 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
492 493 user.permissions[RK][r_k] = p
493 494
494 495 # REPO GROUP
495 496 #==================================================================
496 497 # get access for this user for repos group and override defaults
497 498 #==================================================================
498 499
499 500 # user explicit permissions for repository
500 501 user_repo_groups_perms = \
501 502 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
502 503 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
503 504 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
504 505 .filter(UserRepoGroupToPerm.user_id == uid)\
505 506 .all()
506 507
507 508 for perm in user_repo_groups_perms:
508 509 rg_k = perm.UserRepoGroupToPerm.group.group_name
509 510 p = perm.Permission.permission_name
510 511 cur_perm = user.permissions[GK][rg_k]
511 512 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
512 513 user.permissions[GK][rg_k] = p
513 514
514 515 # REPO GROUP + USER GROUP
515 516 #==================================================================
516 517 # check if user is part of user groups for this repo group and
517 518 # fill in (or replace with higher) permissions
518 519 #==================================================================
519 520
520 521 # users group for repositories permissions
521 522 user_repo_group_perms_from_users_groups = \
522 523 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
523 524 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
524 525 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
525 526 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
526 527 .filter(UsersGroupMember.user_id == uid)\
527 528 .all()
528 529
529 530 for perm in user_repo_group_perms_from_users_groups:
530 531 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
531 532 p = perm.Permission.permission_name
532 533 cur_perm = user.permissions[GK][g_k]
533 534 # overwrite permission only if it's greater than permission
534 535 # given from other sources
535 536 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
536 537 user.permissions[GK][g_k] = p
537 538
538 539 return user
539 540
540 541 def has_perm(self, user, perm):
541 542 if not isinstance(perm, Permission):
542 543 raise Exception('perm needs to be an instance of Permission class '
543 544 'got %s instead' % type(perm))
544 545
545 546 user = self._get_user(user)
546 547
547 548 return UserToPerm.query().filter(UserToPerm.user == user)\
548 549 .filter(UserToPerm.permission == perm).scalar() is not None
549 550
550 551 def grant_perm(self, user, perm):
551 552 """
552 553 Grant user global permissions
553 554
554 555 :param user:
555 556 :param perm:
556 557 """
557 558 user = self._get_user(user)
558 559 perm = self._get_perm(perm)
559 560 # if this permission is already granted skip it
560 561 _perm = UserToPerm.query()\
561 562 .filter(UserToPerm.user == user)\
562 563 .filter(UserToPerm.permission == perm)\
563 564 .scalar()
564 565 if _perm:
565 566 return
566 567 new = UserToPerm()
567 568 new.user = user
568 569 new.permission = perm
569 570 self.sa.add(new)
570 571
571 572 def revoke_perm(self, user, perm):
572 573 """
573 574 Revoke users global permissions
574 575
575 576 :param user:
576 577 :param perm:
577 578 """
578 579 user = self._get_user(user)
579 580 perm = self._get_perm(perm)
580 581
581 582 obj = UserToPerm.query()\
582 583 .filter(UserToPerm.user == user)\
583 584 .filter(UserToPerm.permission == perm)\
584 585 .scalar()
585 586 if obj:
586 587 self.sa.delete(obj)
587 588
588 589 def add_extra_email(self, user, email):
589 590 """
590 591 Adds email address to UserEmailMap
591 592
592 593 :param user:
593 594 :param email:
594 595 """
595 596 from rhodecode.model import forms
596 597 form = forms.UserExtraEmailForm()()
597 598 data = form.to_python(dict(email=email))
598 599 user = self._get_user(user)
599 600
600 601 obj = UserEmailMap()
601 602 obj.user = user
602 603 obj.email = data['email']
603 604 self.sa.add(obj)
604 605 return obj
605 606
606 607 def delete_extra_email(self, user, email_id):
607 608 """
608 609 Removes email address from UserEmailMap
609 610
610 611 :param user:
611 612 :param email_id:
612 613 """
613 614 user = self._get_user(user)
614 615 obj = UserEmailMap.query().get(email_id)
615 616 if obj:
616 617 self.sa.delete(obj)
General Comments 0
You need to be logged in to leave comments. Login now