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