##// END OF EJS Templates
user: speed up data propagatation for auth users by pre-filling only selected variables...
marcink -
r4018:e79bd088 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,979 +1,1000 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 users model for RhodeCode
23 23 """
24 24
25 25 import logging
26 26 import traceback
27 27 import datetime
28 28 import ipaddress
29 29
30 30 from pyramid.threadlocal import get_current_request
31 31 from sqlalchemy.exc import DatabaseError
32 32
33 33 from rhodecode import events
34 34 from rhodecode.lib.user_log_filter import user_log_filter
35 35 from rhodecode.lib.utils2 import (
36 36 safe_unicode, get_current_rhodecode_user, action_logger_generic,
37 37 AttributeDict, str2bool)
38 38 from rhodecode.lib.exceptions import (
39 39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
40 40 UserOwnsUserGroupsException, NotAllowedToCreateUserError, UserOwnsArtifactsException)
41 41 from rhodecode.lib.caching_query import FromCache
42 42 from rhodecode.model import BaseModel
43 43 from rhodecode.model.auth_token import AuthTokenModel
44 44 from rhodecode.model.db import (
45 45 _hash_key, true, false, or_, joinedload, User, UserToPerm,
46 46 UserEmailMap, UserIpMap, UserLog)
47 47 from rhodecode.model.meta import Session
48 48 from rhodecode.model.repo_group import RepoGroupModel
49 49
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 class UserModel(BaseModel):
55 55 cls = User
56 56
57 57 def get(self, user_id, cache=False):
58 58 user = self.sa.query(User)
59 59 if cache:
60 60 user = user.options(
61 61 FromCache("sql_cache_short", "get_user_%s" % user_id))
62 62 return user.get(user_id)
63 63
64 64 def get_user(self, user):
65 65 return self._get_user(user)
66 66
67 67 def _serialize_user(self, user):
68 68 import rhodecode.lib.helpers as h
69 69
70 70 return {
71 71 'id': user.user_id,
72 72 'first_name': user.first_name,
73 73 'last_name': user.last_name,
74 74 'username': user.username,
75 75 'email': user.email,
76 76 'icon_link': h.gravatar_url(user.email, 30),
77 77 'profile_link': h.link_to_user(user),
78 78 'value_display': h.escape(h.person(user)),
79 79 'value': user.username,
80 80 'value_type': 'user',
81 81 'active': user.active,
82 82 }
83 83
84 84 def get_users(self, name_contains=None, limit=20, only_active=True):
85 85
86 86 query = self.sa.query(User)
87 87 if only_active:
88 88 query = query.filter(User.active == true())
89 89
90 90 if name_contains:
91 91 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
92 92 query = query.filter(
93 93 or_(
94 94 User.name.ilike(ilike_expression),
95 95 User.lastname.ilike(ilike_expression),
96 96 User.username.ilike(ilike_expression)
97 97 )
98 98 )
99 99 query = query.limit(limit)
100 100 users = query.all()
101 101
102 102 _users = [
103 103 self._serialize_user(user) for user in users
104 104 ]
105 105 return _users
106 106
107 107 def get_by_username(self, username, cache=False, case_insensitive=False):
108 108
109 109 if case_insensitive:
110 110 user = self.sa.query(User).filter(User.username.ilike(username))
111 111 else:
112 112 user = self.sa.query(User)\
113 113 .filter(User.username == username)
114 114 if cache:
115 115 name_key = _hash_key(username)
116 116 user = user.options(
117 117 FromCache("sql_cache_short", "get_user_%s" % name_key))
118 118 return user.scalar()
119 119
120 120 def get_by_email(self, email, cache=False, case_insensitive=False):
121 121 return User.get_by_email(email, case_insensitive, cache)
122 122
123 123 def get_by_auth_token(self, auth_token, cache=False):
124 124 return User.get_by_auth_token(auth_token, cache)
125 125
126 126 def get_active_user_count(self, cache=False):
127 127 qry = User.query().filter(
128 128 User.active == true()).filter(
129 129 User.username != User.DEFAULT_USER)
130 130 if cache:
131 131 qry = qry.options(
132 132 FromCache("sql_cache_short", "get_active_users"))
133 133 return qry.count()
134 134
135 135 def create(self, form_data, cur_user=None):
136 136 if not cur_user:
137 137 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
138 138
139 139 user_data = {
140 140 'username': form_data['username'],
141 141 'password': form_data['password'],
142 142 'email': form_data['email'],
143 143 'firstname': form_data['firstname'],
144 144 'lastname': form_data['lastname'],
145 145 'active': form_data['active'],
146 146 'extern_type': form_data['extern_type'],
147 147 'extern_name': form_data['extern_name'],
148 148 'admin': False,
149 149 'cur_user': cur_user
150 150 }
151 151
152 152 if 'create_repo_group' in form_data:
153 153 user_data['create_repo_group'] = str2bool(
154 154 form_data.get('create_repo_group'))
155 155
156 156 try:
157 157 if form_data.get('password_change'):
158 158 user_data['force_password_change'] = True
159 159 return UserModel().create_or_update(**user_data)
160 160 except Exception:
161 161 log.error(traceback.format_exc())
162 162 raise
163 163
164 164 def update_user(self, user, skip_attrs=None, **kwargs):
165 165 from rhodecode.lib.auth import get_crypt_password
166 166
167 167 user = self._get_user(user)
168 168 if user.username == User.DEFAULT_USER:
169 169 raise DefaultUserException(
170 170 "You can't edit this user (`%(username)s`) since it's "
171 171 "crucial for entire application" % {
172 172 'username': user.username})
173 173
174 174 # first store only defaults
175 175 user_attrs = {
176 176 'updating_user_id': user.user_id,
177 177 'username': user.username,
178 178 'password': user.password,
179 179 'email': user.email,
180 180 'firstname': user.name,
181 181 'lastname': user.lastname,
182 182 'active': user.active,
183 183 'admin': user.admin,
184 184 'extern_name': user.extern_name,
185 185 'extern_type': user.extern_type,
186 186 'language': user.user_data.get('language')
187 187 }
188 188
189 189 # in case there's new_password, that comes from form, use it to
190 190 # store password
191 191 if kwargs.get('new_password'):
192 192 kwargs['password'] = kwargs['new_password']
193 193
194 194 # cleanups, my_account password change form
195 195 kwargs.pop('current_password', None)
196 196 kwargs.pop('new_password', None)
197 197
198 198 # cleanups, user edit password change form
199 199 kwargs.pop('password_confirmation', None)
200 200 kwargs.pop('password_change', None)
201 201
202 202 # create repo group on user creation
203 203 kwargs.pop('create_repo_group', None)
204 204
205 205 # legacy forms send name, which is the firstname
206 206 firstname = kwargs.pop('name', None)
207 207 if firstname:
208 208 kwargs['firstname'] = firstname
209 209
210 210 for k, v in kwargs.items():
211 211 # skip if we don't want to update this
212 212 if skip_attrs and k in skip_attrs:
213 213 continue
214 214
215 215 user_attrs[k] = v
216 216
217 217 try:
218 218 return self.create_or_update(**user_attrs)
219 219 except Exception:
220 220 log.error(traceback.format_exc())
221 221 raise
222 222
223 223 def create_or_update(
224 224 self, username, password, email, firstname='', lastname='',
225 225 active=True, admin=False, extern_type=None, extern_name=None,
226 226 cur_user=None, plugin=None, force_password_change=False,
227 227 allow_to_create_user=True, create_repo_group=None,
228 228 updating_user_id=None, language=None, strict_creation_check=True):
229 229 """
230 230 Creates a new instance if not found, or updates current one
231 231
232 232 :param username:
233 233 :param password:
234 234 :param email:
235 235 :param firstname:
236 236 :param lastname:
237 237 :param active:
238 238 :param admin:
239 239 :param extern_type:
240 240 :param extern_name:
241 241 :param cur_user:
242 242 :param plugin: optional plugin this method was called from
243 243 :param force_password_change: toggles new or existing user flag
244 244 for password change
245 245 :param allow_to_create_user: Defines if the method can actually create
246 246 new users
247 247 :param create_repo_group: Defines if the method should also
248 248 create an repo group with user name, and owner
249 249 :param updating_user_id: if we set it up this is the user we want to
250 250 update this allows to editing username.
251 251 :param language: language of user from interface.
252 252
253 253 :returns: new User object with injected `is_new_user` attribute.
254 254 """
255 255
256 256 if not cur_user:
257 257 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
258 258
259 259 from rhodecode.lib.auth import (
260 260 get_crypt_password, check_password, generate_auth_token)
261 261 from rhodecode.lib.hooks_base import (
262 262 log_create_user, check_allowed_create_user)
263 263
264 264 def _password_change(new_user, password):
265 265 old_password = new_user.password or ''
266 266 # empty password
267 267 if not old_password:
268 268 return False
269 269
270 270 # password check is only needed for RhodeCode internal auth calls
271 271 # in case it's a plugin we don't care
272 272 if not plugin:
273 273
274 274 # first check if we gave crypted password back, and if it
275 275 # matches it's not password change
276 276 if new_user.password == password:
277 277 return False
278 278
279 279 password_match = check_password(password, old_password)
280 280 if not password_match:
281 281 return True
282 282
283 283 return False
284 284
285 285 # read settings on default personal repo group creation
286 286 if create_repo_group is None:
287 287 default_create_repo_group = RepoGroupModel()\
288 288 .get_default_create_personal_repo_group()
289 289 create_repo_group = default_create_repo_group
290 290
291 291 user_data = {
292 292 'username': username,
293 293 'password': password,
294 294 'email': email,
295 295 'firstname': firstname,
296 296 'lastname': lastname,
297 297 'active': active,
298 298 'admin': admin
299 299 }
300 300
301 301 if updating_user_id:
302 302 log.debug('Checking for existing account in RhodeCode '
303 303 'database with user_id `%s` ', updating_user_id)
304 304 user = User.get(updating_user_id)
305 305 else:
306 306 log.debug('Checking for existing account in RhodeCode '
307 307 'database with username `%s` ', username)
308 308 user = User.get_by_username(username, case_insensitive=True)
309 309
310 310 if user is None:
311 311 # we check internal flag if this method is actually allowed to
312 312 # create new user
313 313 if not allow_to_create_user:
314 314 msg = ('Method wants to create new user, but it is not '
315 315 'allowed to do so')
316 316 log.warning(msg)
317 317 raise NotAllowedToCreateUserError(msg)
318 318
319 319 log.debug('Creating new user %s', username)
320 320
321 321 # only if we create user that is active
322 322 new_active_user = active
323 323 if new_active_user and strict_creation_check:
324 324 # raises UserCreationError if it's not allowed for any reason to
325 325 # create new active user, this also executes pre-create hooks
326 326 check_allowed_create_user(user_data, cur_user, strict_check=True)
327 327 events.trigger(events.UserPreCreate(user_data))
328 328 new_user = User()
329 329 edit = False
330 330 else:
331 331 log.debug('updating user `%s`', username)
332 332 events.trigger(events.UserPreUpdate(user, user_data))
333 333 new_user = user
334 334 edit = True
335 335
336 336 # we're not allowed to edit default user
337 337 if user.username == User.DEFAULT_USER:
338 338 raise DefaultUserException(
339 339 "You can't edit this user (`%(username)s`) since it's "
340 340 "crucial for entire application"
341 341 % {'username': user.username})
342 342
343 343 # inject special attribute that will tell us if User is new or old
344 344 new_user.is_new_user = not edit
345 345 # for users that didn's specify auth type, we use RhodeCode built in
346 346 from rhodecode.authentication.plugins import auth_rhodecode
347 347 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.uid
348 348 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.uid
349 349
350 350 try:
351 351 new_user.username = username
352 352 new_user.admin = admin
353 353 new_user.email = email
354 354 new_user.active = active
355 355 new_user.extern_name = safe_unicode(extern_name)
356 356 new_user.extern_type = safe_unicode(extern_type)
357 357 new_user.name = firstname
358 358 new_user.lastname = lastname
359 359
360 360 # set password only if creating an user or password is changed
361 361 if not edit or _password_change(new_user, password):
362 362 reason = 'new password' if edit else 'new user'
363 363 log.debug('Updating password reason=>%s', reason)
364 364 new_user.password = get_crypt_password(password) if password else None
365 365
366 366 if force_password_change:
367 367 new_user.update_userdata(force_password_change=True)
368 368 if language:
369 369 new_user.update_userdata(language=language)
370 370 new_user.update_userdata(notification_status=True)
371 371
372 372 self.sa.add(new_user)
373 373
374 374 if not edit and create_repo_group:
375 375 RepoGroupModel().create_personal_repo_group(
376 376 new_user, commit_early=False)
377 377
378 378 if not edit:
379 379 # add the RSS token
380 380 self.add_auth_token(
381 381 user=username, lifetime_minutes=-1,
382 382 role=self.auth_token_role.ROLE_FEED,
383 383 description=u'Generated feed token')
384 384
385 385 kwargs = new_user.get_dict()
386 386 # backward compat, require api_keys present
387 387 kwargs['api_keys'] = kwargs['auth_tokens']
388 388 log_create_user(created_by=cur_user, **kwargs)
389 389 events.trigger(events.UserPostCreate(user_data))
390 390 return new_user
391 391 except (DatabaseError,):
392 392 log.error(traceback.format_exc())
393 393 raise
394 394
395 395 def create_registration(self, form_data,
396 396 extern_name='rhodecode', extern_type='rhodecode'):
397 397 from rhodecode.model.notification import NotificationModel
398 398 from rhodecode.model.notification import EmailNotificationModel
399 399
400 400 try:
401 401 form_data['admin'] = False
402 402 form_data['extern_name'] = extern_name
403 403 form_data['extern_type'] = extern_type
404 404 new_user = self.create(form_data)
405 405
406 406 self.sa.add(new_user)
407 407 self.sa.flush()
408 408
409 409 user_data = new_user.get_dict()
410 410 kwargs = {
411 411 # use SQLALCHEMY safe dump of user data
412 412 'user': AttributeDict(user_data),
413 413 'date': datetime.datetime.now()
414 414 }
415 415 notification_type = EmailNotificationModel.TYPE_REGISTRATION
416 416 # pre-generate the subject for notification itself
417 417 (subject,
418 418 _h, _e, # we don't care about those
419 419 body_plaintext) = EmailNotificationModel().render_email(
420 420 notification_type, **kwargs)
421 421
422 422 # create notification objects, and emails
423 423 NotificationModel().create(
424 424 created_by=new_user,
425 425 notification_subject=subject,
426 426 notification_body=body_plaintext,
427 427 notification_type=notification_type,
428 428 recipients=None, # all admins
429 429 email_kwargs=kwargs,
430 430 )
431 431
432 432 return new_user
433 433 except Exception:
434 434 log.error(traceback.format_exc())
435 435 raise
436 436
437 437 def _handle_user_repos(self, username, repositories, handle_mode=None):
438 438 _superadmin = self.cls.get_first_super_admin()
439 439 left_overs = True
440 440
441 441 from rhodecode.model.repo import RepoModel
442 442
443 443 if handle_mode == 'detach':
444 444 for obj in repositories:
445 445 obj.user = _superadmin
446 446 # set description we know why we super admin now owns
447 447 # additional repositories that were orphaned !
448 448 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
449 449 self.sa.add(obj)
450 450 left_overs = False
451 451 elif handle_mode == 'delete':
452 452 for obj in repositories:
453 453 RepoModel().delete(obj, forks='detach')
454 454 left_overs = False
455 455
456 456 # if nothing is done we have left overs left
457 457 return left_overs
458 458
459 459 def _handle_user_repo_groups(self, username, repository_groups,
460 460 handle_mode=None):
461 461 _superadmin = self.cls.get_first_super_admin()
462 462 left_overs = True
463 463
464 464 from rhodecode.model.repo_group import RepoGroupModel
465 465
466 466 if handle_mode == 'detach':
467 467 for r in repository_groups:
468 468 r.user = _superadmin
469 469 # set description we know why we super admin now owns
470 470 # additional repositories that were orphaned !
471 471 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
472 472 r.personal = False
473 473 self.sa.add(r)
474 474 left_overs = False
475 475 elif handle_mode == 'delete':
476 476 for r in repository_groups:
477 477 RepoGroupModel().delete(r)
478 478 left_overs = False
479 479
480 480 # if nothing is done we have left overs left
481 481 return left_overs
482 482
483 483 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
484 484 _superadmin = self.cls.get_first_super_admin()
485 485 left_overs = True
486 486
487 487 from rhodecode.model.user_group import UserGroupModel
488 488
489 489 if handle_mode == 'detach':
490 490 for r in user_groups:
491 491 for user_user_group_to_perm in r.user_user_group_to_perm:
492 492 if user_user_group_to_perm.user.username == username:
493 493 user_user_group_to_perm.user = _superadmin
494 494 r.user = _superadmin
495 495 # set description we know why we super admin now owns
496 496 # additional repositories that were orphaned !
497 497 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
498 498 self.sa.add(r)
499 499 left_overs = False
500 500 elif handle_mode == 'delete':
501 501 for r in user_groups:
502 502 UserGroupModel().delete(r)
503 503 left_overs = False
504 504
505 505 # if nothing is done we have left overs left
506 506 return left_overs
507 507
508 508 def _handle_user_artifacts(self, username, artifacts, handle_mode=None):
509 509 _superadmin = self.cls.get_first_super_admin()
510 510 left_overs = True
511 511
512 512 if handle_mode == 'detach':
513 513 for a in artifacts:
514 514 a.upload_user = _superadmin
515 515 # set description we know why we super admin now owns
516 516 # additional artifacts that were orphaned !
517 517 a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
518 518 self.sa.add(a)
519 519 left_overs = False
520 520 elif handle_mode == 'delete':
521 521 from rhodecode.apps.file_store import utils as store_utils
522 522 storage = store_utils.get_file_storage(self.request.registry.settings)
523 523 for a in artifacts:
524 524 file_uid = a.file_uid
525 525 storage.delete(file_uid)
526 526 self.sa.delete(a)
527 527
528 528 left_overs = False
529 529
530 530 # if nothing is done we have left overs left
531 531 return left_overs
532 532
533 533 def delete(self, user, cur_user=None, handle_repos=None,
534 534 handle_repo_groups=None, handle_user_groups=None, handle_artifacts=None):
535 535 from rhodecode.lib.hooks_base import log_delete_user
536 536
537 537 if not cur_user:
538 538 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
539 539 user = self._get_user(user)
540 540
541 541 try:
542 542 if user.username == User.DEFAULT_USER:
543 543 raise DefaultUserException(
544 544 u"You can't remove this user since it's"
545 545 u" crucial for entire application")
546 546
547 547 left_overs = self._handle_user_repos(
548 548 user.username, user.repositories, handle_repos)
549 549 if left_overs and user.repositories:
550 550 repos = [x.repo_name for x in user.repositories]
551 551 raise UserOwnsReposException(
552 552 u'user "%(username)s" still owns %(len_repos)s repositories and cannot be '
553 553 u'removed. Switch owners or remove those repositories:%(list_repos)s'
554 554 % {'username': user.username, 'len_repos': len(repos),
555 555 'list_repos': ', '.join(repos)})
556 556
557 557 left_overs = self._handle_user_repo_groups(
558 558 user.username, user.repository_groups, handle_repo_groups)
559 559 if left_overs and user.repository_groups:
560 560 repo_groups = [x.group_name for x in user.repository_groups]
561 561 raise UserOwnsRepoGroupsException(
562 562 u'user "%(username)s" still owns %(len_repo_groups)s repository groups and cannot be '
563 563 u'removed. Switch owners or remove those repository groups:%(list_repo_groups)s'
564 564 % {'username': user.username, 'len_repo_groups': len(repo_groups),
565 565 'list_repo_groups': ', '.join(repo_groups)})
566 566
567 567 left_overs = self._handle_user_user_groups(
568 568 user.username, user.user_groups, handle_user_groups)
569 569 if left_overs and user.user_groups:
570 570 user_groups = [x.users_group_name for x in user.user_groups]
571 571 raise UserOwnsUserGroupsException(
572 572 u'user "%s" still owns %s user groups and cannot be '
573 573 u'removed. Switch owners or remove those user groups:%s'
574 574 % (user.username, len(user_groups), ', '.join(user_groups)))
575 575
576 576 left_overs = self._handle_user_artifacts(
577 577 user.username, user.artifacts, handle_artifacts)
578 578 if left_overs and user.artifacts:
579 579 artifacts = [x.file_uid for x in user.artifacts]
580 580 raise UserOwnsArtifactsException(
581 581 u'user "%s" still owns %s artifacts and cannot be '
582 582 u'removed. Switch owners or remove those artifacts:%s'
583 583 % (user.username, len(artifacts), ', '.join(artifacts)))
584 584
585 585 user_data = user.get_dict() # fetch user data before expire
586 586
587 587 # we might change the user data with detach/delete, make sure
588 588 # the object is marked as expired before actually deleting !
589 589 self.sa.expire(user)
590 590 self.sa.delete(user)
591 591
592 592 log_delete_user(deleted_by=cur_user, **user_data)
593 593 except Exception:
594 594 log.error(traceback.format_exc())
595 595 raise
596 596
597 597 def reset_password_link(self, data, pwd_reset_url):
598 598 from rhodecode.lib.celerylib import tasks, run_task
599 599 from rhodecode.model.notification import EmailNotificationModel
600 600 user_email = data['email']
601 601 try:
602 602 user = User.get_by_email(user_email)
603 603 if user:
604 604 log.debug('password reset user found %s', user)
605 605
606 606 email_kwargs = {
607 607 'password_reset_url': pwd_reset_url,
608 608 'user': user,
609 609 'email': user_email,
610 610 'date': datetime.datetime.now()
611 611 }
612 612
613 613 (subject, headers, email_body,
614 614 email_body_plaintext) = EmailNotificationModel().render_email(
615 615 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
616 616
617 617 recipients = [user_email]
618 618
619 619 action_logger_generic(
620 620 'sending password reset email to user: {}'.format(
621 621 user), namespace='security.password_reset')
622 622
623 623 run_task(tasks.send_email, recipients, subject,
624 624 email_body_plaintext, email_body)
625 625
626 626 else:
627 627 log.debug("password reset email %s not found", user_email)
628 628 except Exception:
629 629 log.error(traceback.format_exc())
630 630 return False
631 631
632 632 return True
633 633
634 634 def reset_password(self, data):
635 635 from rhodecode.lib.celerylib import tasks, run_task
636 636 from rhodecode.model.notification import EmailNotificationModel
637 637 from rhodecode.lib import auth
638 638 user_email = data['email']
639 639 pre_db = True
640 640 try:
641 641 user = User.get_by_email(user_email)
642 642 new_passwd = auth.PasswordGenerator().gen_password(
643 643 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
644 644 if user:
645 645 user.password = auth.get_crypt_password(new_passwd)
646 646 # also force this user to reset his password !
647 647 user.update_userdata(force_password_change=True)
648 648
649 649 Session().add(user)
650 650
651 651 # now delete the token in question
652 652 UserApiKeys = AuthTokenModel.cls
653 653 UserApiKeys().query().filter(
654 654 UserApiKeys.api_key == data['token']).delete()
655 655
656 656 Session().commit()
657 657 log.info('successfully reset password for `%s`', user_email)
658 658
659 659 if new_passwd is None:
660 660 raise Exception('unable to generate new password')
661 661
662 662 pre_db = False
663 663
664 664 email_kwargs = {
665 665 'new_password': new_passwd,
666 666 'user': user,
667 667 'email': user_email,
668 668 'date': datetime.datetime.now()
669 669 }
670 670
671 671 (subject, headers, email_body,
672 672 email_body_plaintext) = EmailNotificationModel().render_email(
673 673 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
674 674 **email_kwargs)
675 675
676 676 recipients = [user_email]
677 677
678 678 action_logger_generic(
679 679 'sent new password to user: {} with email: {}'.format(
680 680 user, user_email), namespace='security.password_reset')
681 681
682 682 run_task(tasks.send_email, recipients, subject,
683 683 email_body_plaintext, email_body)
684 684
685 685 except Exception:
686 686 log.error('Failed to update user password')
687 687 log.error(traceback.format_exc())
688 688 if pre_db:
689 689 # we rollback only if local db stuff fails. If it goes into
690 690 # run_task, we're pass rollback state this wouldn't work then
691 691 Session().rollback()
692 692
693 693 return True
694 694
695 695 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
696 696 """
697 697 Fetches auth_user by user_id,or api_key if present.
698 698 Fills auth_user attributes with those taken from database.
699 699 Additionally set's is_authenitated if lookup fails
700 700 present in database
701 701
702 702 :param auth_user: instance of user to set attributes
703 703 :param user_id: user id to fetch by
704 704 :param api_key: api key to fetch by
705 705 :param username: username to fetch by
706 706 """
707 707 def token_obfuscate(token):
708 708 if token:
709 709 return token[:4] + "****"
710 710
711 711 if user_id is None and api_key is None and username is None:
712 712 raise Exception('You need to pass user_id, api_key or username')
713 713
714 714 log.debug(
715 715 'AuthUser: fill data execution based on: '
716 716 'user_id:%s api_key:%s username:%s', user_id, api_key, username)
717 717 try:
718 718 dbuser = None
719 719 if user_id:
720 720 dbuser = self.get(user_id)
721 721 elif api_key:
722 722 dbuser = self.get_by_auth_token(api_key)
723 723 elif username:
724 724 dbuser = self.get_by_username(username)
725 725
726 726 if not dbuser:
727 727 log.warning(
728 728 'Unable to lookup user by id:%s api_key:%s username:%s',
729 729 user_id, token_obfuscate(api_key), username)
730 730 return False
731 731 if not dbuser.active:
732 732 log.debug('User `%s:%s` is inactive, skipping fill data',
733 733 username, user_id)
734 734 return False
735 735
736 736 log.debug('AuthUser: filling found user:%s data', dbuser)
737 user_data = dbuser.get_dict()
738 737
739 user_data.update({
740 # set explicit the safe escaped values
738 attrs = {
739 'user_id': dbuser.user_id,
740 'username': dbuser.username,
741 'name': dbuser.name,
741 742 'first_name': dbuser.first_name,
743 'firstname': dbuser.firstname,
742 744 'last_name': dbuser.last_name,
743 })
745 'lastname': dbuser.lastname,
746 'admin': dbuser.admin,
747 'active': dbuser.active,
748
749 'email': dbuser.email,
750 'emails': dbuser.emails_cached(),
751 'short_contact': dbuser.short_contact,
752 'full_contact': dbuser.full_contact,
753 'full_name': dbuser.full_name,
754 'full_name_or_username': dbuser.full_name_or_username,
744 755
745 for k, v in user_data.items():
746 # properties of auth user we dont update
747 if k not in ['auth_tokens', 'permissions']:
748 setattr(auth_user, k, v)
756 '_api_key': dbuser._api_key,
757 '_user_data': dbuser._user_data,
758
759 'created_on': dbuser.created_on,
760 'extern_name': dbuser.extern_name,
761 'extern_type': dbuser.extern_type,
749 762
763 'inherit_default_permissions': dbuser.inherit_default_permissions,
764
765 'language': dbuser.language,
766 'last_activity': dbuser.last_activity,
767 'last_login': dbuser.last_login,
768 'password': dbuser.password,
769 }
770 auth_user.__dict__.update(attrs)
750 771 except Exception:
751 772 log.error(traceback.format_exc())
752 773 auth_user.is_authenticated = False
753 774 return False
754 775
755 776 return True
756 777
757 778 def has_perm(self, user, perm):
758 779 perm = self._get_perm(perm)
759 780 user = self._get_user(user)
760 781
761 782 return UserToPerm.query().filter(UserToPerm.user == user)\
762 783 .filter(UserToPerm.permission == perm).scalar() is not None
763 784
764 785 def grant_perm(self, user, perm):
765 786 """
766 787 Grant user global permissions
767 788
768 789 :param user:
769 790 :param perm:
770 791 """
771 792 user = self._get_user(user)
772 793 perm = self._get_perm(perm)
773 794 # if this permission is already granted skip it
774 795 _perm = UserToPerm.query()\
775 796 .filter(UserToPerm.user == user)\
776 797 .filter(UserToPerm.permission == perm)\
777 798 .scalar()
778 799 if _perm:
779 800 return
780 801 new = UserToPerm()
781 802 new.user = user
782 803 new.permission = perm
783 804 self.sa.add(new)
784 805 return new
785 806
786 807 def revoke_perm(self, user, perm):
787 808 """
788 809 Revoke users global permissions
789 810
790 811 :param user:
791 812 :param perm:
792 813 """
793 814 user = self._get_user(user)
794 815 perm = self._get_perm(perm)
795 816
796 817 obj = UserToPerm.query()\
797 818 .filter(UserToPerm.user == user)\
798 819 .filter(UserToPerm.permission == perm)\
799 820 .scalar()
800 821 if obj:
801 822 self.sa.delete(obj)
802 823
803 824 def add_extra_email(self, user, email):
804 825 """
805 826 Adds email address to UserEmailMap
806 827
807 828 :param user:
808 829 :param email:
809 830 """
810 831
811 832 user = self._get_user(user)
812 833
813 834 obj = UserEmailMap()
814 835 obj.user = user
815 836 obj.email = email
816 837 self.sa.add(obj)
817 838 return obj
818 839
819 840 def delete_extra_email(self, user, email_id):
820 841 """
821 842 Removes email address from UserEmailMap
822 843
823 844 :param user:
824 845 :param email_id:
825 846 """
826 847 user = self._get_user(user)
827 848 obj = UserEmailMap.query().get(email_id)
828 849 if obj and obj.user_id == user.user_id:
829 850 self.sa.delete(obj)
830 851
831 852 def parse_ip_range(self, ip_range):
832 853 ip_list = []
833 854
834 855 def make_unique(value):
835 856 seen = []
836 857 return [c for c in value if not (c in seen or seen.append(c))]
837 858
838 859 # firsts split by commas
839 860 for ip_range in ip_range.split(','):
840 861 if not ip_range:
841 862 continue
842 863 ip_range = ip_range.strip()
843 864 if '-' in ip_range:
844 865 start_ip, end_ip = ip_range.split('-', 1)
845 866 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
846 867 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
847 868 parsed_ip_range = []
848 869
849 870 for index in xrange(int(start_ip), int(end_ip) + 1):
850 871 new_ip = ipaddress.ip_address(index)
851 872 parsed_ip_range.append(str(new_ip))
852 873 ip_list.extend(parsed_ip_range)
853 874 else:
854 875 ip_list.append(ip_range)
855 876
856 877 return make_unique(ip_list)
857 878
858 879 def add_extra_ip(self, user, ip, description=None):
859 880 """
860 881 Adds ip address to UserIpMap
861 882
862 883 :param user:
863 884 :param ip:
864 885 """
865 886
866 887 user = self._get_user(user)
867 888 obj = UserIpMap()
868 889 obj.user = user
869 890 obj.ip_addr = ip
870 891 obj.description = description
871 892 self.sa.add(obj)
872 893 return obj
873 894
874 895 auth_token_role = AuthTokenModel.cls
875 896
876 897 def add_auth_token(self, user, lifetime_minutes, role, description=u'',
877 898 scope_callback=None):
878 899 """
879 900 Add AuthToken for user.
880 901
881 902 :param user: username/user_id
882 903 :param lifetime_minutes: in minutes the lifetime for token, -1 equals no limit
883 904 :param role: one of AuthTokenModel.cls.ROLE_*
884 905 :param description: optional string description
885 906 """
886 907
887 908 token = AuthTokenModel().create(
888 909 user, description, lifetime_minutes, role)
889 910 if scope_callback and callable(scope_callback):
890 911 # call the callback if we provide, used to attach scope for EE edition
891 912 scope_callback(token)
892 913 return token
893 914
894 915 def delete_extra_ip(self, user, ip_id):
895 916 """
896 917 Removes ip address from UserIpMap
897 918
898 919 :param user:
899 920 :param ip_id:
900 921 """
901 922 user = self._get_user(user)
902 923 obj = UserIpMap.query().get(ip_id)
903 924 if obj and obj.user_id == user.user_id:
904 925 self.sa.delete(obj)
905 926
906 927 def get_accounts_in_creation_order(self, current_user=None):
907 928 """
908 929 Get accounts in order of creation for deactivation for license limits
909 930
910 931 pick currently logged in user, and append to the list in position 0
911 932 pick all super-admins in order of creation date and add it to the list
912 933 pick all other accounts in order of creation and add it to the list.
913 934
914 935 Based on that list, the last accounts can be disabled as they are
915 936 created at the end and don't include any of the super admins as well
916 937 as the current user.
917 938
918 939 :param current_user: optionally current user running this operation
919 940 """
920 941
921 942 if not current_user:
922 943 current_user = get_current_rhodecode_user()
923 944 active_super_admins = [
924 945 x.user_id for x in User.query()
925 946 .filter(User.user_id != current_user.user_id)
926 947 .filter(User.active == true())
927 948 .filter(User.admin == true())
928 949 .order_by(User.created_on.asc())]
929 950
930 951 active_regular_users = [
931 952 x.user_id for x in User.query()
932 953 .filter(User.user_id != current_user.user_id)
933 954 .filter(User.active == true())
934 955 .filter(User.admin == false())
935 956 .order_by(User.created_on.asc())]
936 957
937 958 list_of_accounts = [current_user.user_id]
938 959 list_of_accounts += active_super_admins
939 960 list_of_accounts += active_regular_users
940 961
941 962 return list_of_accounts
942 963
943 964 def deactivate_last_users(self, expected_users, current_user=None):
944 965 """
945 966 Deactivate accounts that are over the license limits.
946 967 Algorithm of which accounts to disabled is based on the formula:
947 968
948 969 Get current user, then super admins in creation order, then regular
949 970 active users in creation order.
950 971
951 972 Using that list we mark all accounts from the end of it as inactive.
952 973 This way we block only latest created accounts.
953 974
954 975 :param expected_users: list of users in special order, we deactivate
955 976 the end N amount of users from that list
956 977 """
957 978
958 979 list_of_accounts = self.get_accounts_in_creation_order(
959 980 current_user=current_user)
960 981
961 982 for acc_id in list_of_accounts[expected_users + 1:]:
962 983 user = User.get(acc_id)
963 984 log.info('Deactivating account %s for license unlock', user)
964 985 user.active = False
965 986 Session().add(user)
966 987 Session().commit()
967 988
968 989 return
969 990
970 991 def get_user_log(self, user, filter_term):
971 992 user_log = UserLog.query()\
972 993 .filter(or_(UserLog.user_id == user.user_id,
973 994 UserLog.username == user.username))\
974 995 .options(joinedload(UserLog.user))\
975 996 .options(joinedload(UserLog.repository))\
976 997 .order_by(UserLog.action_date.desc())
977 998
978 999 user_log = user_log_filter(user_log, filter_term)
979 1000 return user_log
General Comments 0
You need to be logged in to leave comments. Login now