##// END OF EJS Templates
users: when deleting users ensure we also clear personal flag so we don't have multiple personal groups...
marcink -
r3086:e07c3d91 default
parent child Browse files
Show More
@@ -1,940 +1,941 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 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)
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.name
348 348 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
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 from rhodecode.model.notification import NotificationModel
397 397 from rhodecode.model.notification import EmailNotificationModel
398 398
399 399 try:
400 400 form_data['admin'] = False
401 401 form_data['extern_name'] = 'rhodecode'
402 402 form_data['extern_type'] = 'rhodecode'
403 403 new_user = self.create(form_data)
404 404
405 405 self.sa.add(new_user)
406 406 self.sa.flush()
407 407
408 408 user_data = new_user.get_dict()
409 409 kwargs = {
410 410 # use SQLALCHEMY safe dump of user data
411 411 'user': AttributeDict(user_data),
412 412 'date': datetime.datetime.now()
413 413 }
414 414 notification_type = EmailNotificationModel.TYPE_REGISTRATION
415 415 # pre-generate the subject for notification itself
416 416 (subject,
417 417 _h, _e, # we don't care about those
418 418 body_plaintext) = EmailNotificationModel().render_email(
419 419 notification_type, **kwargs)
420 420
421 421 # create notification objects, and emails
422 422 NotificationModel().create(
423 423 created_by=new_user,
424 424 notification_subject=subject,
425 425 notification_body=body_plaintext,
426 426 notification_type=notification_type,
427 427 recipients=None, # all admins
428 428 email_kwargs=kwargs,
429 429 )
430 430
431 431 return new_user
432 432 except Exception:
433 433 log.error(traceback.format_exc())
434 434 raise
435 435
436 436 def _handle_user_repos(self, username, repositories, handle_mode=None):
437 437 _superadmin = self.cls.get_first_super_admin()
438 438 left_overs = True
439 439
440 440 from rhodecode.model.repo import RepoModel
441 441
442 442 if handle_mode == 'detach':
443 443 for obj in repositories:
444 444 obj.user = _superadmin
445 445 # set description we know why we super admin now owns
446 446 # additional repositories that were orphaned !
447 447 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
448 448 self.sa.add(obj)
449 449 left_overs = False
450 450 elif handle_mode == 'delete':
451 451 for obj in repositories:
452 452 RepoModel().delete(obj, forks='detach')
453 453 left_overs = False
454 454
455 455 # if nothing is done we have left overs left
456 456 return left_overs
457 457
458 458 def _handle_user_repo_groups(self, username, repository_groups,
459 459 handle_mode=None):
460 460 _superadmin = self.cls.get_first_super_admin()
461 461 left_overs = True
462 462
463 463 from rhodecode.model.repo_group import RepoGroupModel
464 464
465 465 if handle_mode == 'detach':
466 466 for r in repository_groups:
467 467 r.user = _superadmin
468 468 # set description we know why we super admin now owns
469 469 # additional repositories that were orphaned !
470 470 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
471 r.personal = False
471 472 self.sa.add(r)
472 473 left_overs = False
473 474 elif handle_mode == 'delete':
474 475 for r in repository_groups:
475 476 RepoGroupModel().delete(r)
476 477 left_overs = False
477 478
478 479 # if nothing is done we have left overs left
479 480 return left_overs
480 481
481 482 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
482 483 _superadmin = self.cls.get_first_super_admin()
483 484 left_overs = True
484 485
485 486 from rhodecode.model.user_group import UserGroupModel
486 487
487 488 if handle_mode == 'detach':
488 489 for r in user_groups:
489 490 for user_user_group_to_perm in r.user_user_group_to_perm:
490 491 if user_user_group_to_perm.user.username == username:
491 492 user_user_group_to_perm.user = _superadmin
492 493 r.user = _superadmin
493 494 # set description we know why we super admin now owns
494 495 # additional repositories that were orphaned !
495 496 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
496 497 self.sa.add(r)
497 498 left_overs = False
498 499 elif handle_mode == 'delete':
499 500 for r in user_groups:
500 501 UserGroupModel().delete(r)
501 502 left_overs = False
502 503
503 504 # if nothing is done we have left overs left
504 505 return left_overs
505 506
506 507 def delete(self, user, cur_user=None, handle_repos=None,
507 508 handle_repo_groups=None, handle_user_groups=None):
508 509 if not cur_user:
509 510 cur_user = getattr(
510 511 get_current_rhodecode_user(), 'username', None)
511 512 user = self._get_user(user)
512 513
513 514 try:
514 515 if user.username == User.DEFAULT_USER:
515 516 raise DefaultUserException(
516 517 u"You can't remove this user since it's"
517 518 u" crucial for entire application")
518 519
519 520 left_overs = self._handle_user_repos(
520 521 user.username, user.repositories, handle_repos)
521 522 if left_overs and user.repositories:
522 523 repos = [x.repo_name for x in user.repositories]
523 524 raise UserOwnsReposException(
524 525 u'user "%(username)s" still owns %(len_repos)s repositories and cannot be '
525 526 u'removed. Switch owners or remove those repositories:%(list_repos)s'
526 527 % {'username': user.username, 'len_repos': len(repos),
527 528 'list_repos': ', '.join(repos)})
528 529
529 530 left_overs = self._handle_user_repo_groups(
530 531 user.username, user.repository_groups, handle_repo_groups)
531 532 if left_overs and user.repository_groups:
532 533 repo_groups = [x.group_name for x in user.repository_groups]
533 534 raise UserOwnsRepoGroupsException(
534 535 u'user "%(username)s" still owns %(len_repo_groups)s repository groups and cannot be '
535 536 u'removed. Switch owners or remove those repository groups:%(list_repo_groups)s'
536 537 % {'username': user.username, 'len_repo_groups': len(repo_groups),
537 538 'list_repo_groups': ', '.join(repo_groups)})
538 539
539 540 left_overs = self._handle_user_user_groups(
540 541 user.username, user.user_groups, handle_user_groups)
541 542 if left_overs and user.user_groups:
542 543 user_groups = [x.users_group_name for x in user.user_groups]
543 544 raise UserOwnsUserGroupsException(
544 545 u'user "%s" still owns %s user groups and cannot be '
545 546 u'removed. Switch owners or remove those user groups:%s'
546 547 % (user.username, len(user_groups), ', '.join(user_groups)))
547 548
548 549 # we might change the user data with detach/delete, make sure
549 550 # the object is marked as expired before actually deleting !
550 551 self.sa.expire(user)
551 552 self.sa.delete(user)
552 553 from rhodecode.lib.hooks_base import log_delete_user
553 554 log_delete_user(deleted_by=cur_user, **user.get_dict())
554 555 except Exception:
555 556 log.error(traceback.format_exc())
556 557 raise
557 558
558 559 def reset_password_link(self, data, pwd_reset_url):
559 560 from rhodecode.lib.celerylib import tasks, run_task
560 561 from rhodecode.model.notification import EmailNotificationModel
561 562 user_email = data['email']
562 563 try:
563 564 user = User.get_by_email(user_email)
564 565 if user:
565 566 log.debug('password reset user found %s', user)
566 567
567 568 email_kwargs = {
568 569 'password_reset_url': pwd_reset_url,
569 570 'user': user,
570 571 'email': user_email,
571 572 'date': datetime.datetime.now()
572 573 }
573 574
574 575 (subject, headers, email_body,
575 576 email_body_plaintext) = EmailNotificationModel().render_email(
576 577 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
577 578
578 579 recipients = [user_email]
579 580
580 581 action_logger_generic(
581 582 'sending password reset email to user: {}'.format(
582 583 user), namespace='security.password_reset')
583 584
584 585 run_task(tasks.send_email, recipients, subject,
585 586 email_body_plaintext, email_body)
586 587
587 588 else:
588 589 log.debug("password reset email %s not found", user_email)
589 590 except Exception:
590 591 log.error(traceback.format_exc())
591 592 return False
592 593
593 594 return True
594 595
595 596 def reset_password(self, data):
596 597 from rhodecode.lib.celerylib import tasks, run_task
597 598 from rhodecode.model.notification import EmailNotificationModel
598 599 from rhodecode.lib import auth
599 600 user_email = data['email']
600 601 pre_db = True
601 602 try:
602 603 user = User.get_by_email(user_email)
603 604 new_passwd = auth.PasswordGenerator().gen_password(
604 605 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
605 606 if user:
606 607 user.password = auth.get_crypt_password(new_passwd)
607 608 # also force this user to reset his password !
608 609 user.update_userdata(force_password_change=True)
609 610
610 611 Session().add(user)
611 612
612 613 # now delete the token in question
613 614 UserApiKeys = AuthTokenModel.cls
614 615 UserApiKeys().query().filter(
615 616 UserApiKeys.api_key == data['token']).delete()
616 617
617 618 Session().commit()
618 619 log.info('successfully reset password for `%s`', user_email)
619 620
620 621 if new_passwd is None:
621 622 raise Exception('unable to generate new password')
622 623
623 624 pre_db = False
624 625
625 626 email_kwargs = {
626 627 'new_password': new_passwd,
627 628 'user': user,
628 629 'email': user_email,
629 630 'date': datetime.datetime.now()
630 631 }
631 632
632 633 (subject, headers, email_body,
633 634 email_body_plaintext) = EmailNotificationModel().render_email(
634 635 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
635 636 **email_kwargs)
636 637
637 638 recipients = [user_email]
638 639
639 640 action_logger_generic(
640 641 'sent new password to user: {} with email: {}'.format(
641 642 user, user_email), namespace='security.password_reset')
642 643
643 644 run_task(tasks.send_email, recipients, subject,
644 645 email_body_plaintext, email_body)
645 646
646 647 except Exception:
647 648 log.error('Failed to update user password')
648 649 log.error(traceback.format_exc())
649 650 if pre_db:
650 651 # we rollback only if local db stuff fails. If it goes into
651 652 # run_task, we're pass rollback state this wouldn't work then
652 653 Session().rollback()
653 654
654 655 return True
655 656
656 657 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
657 658 """
658 659 Fetches auth_user by user_id,or api_key if present.
659 660 Fills auth_user attributes with those taken from database.
660 661 Additionally set's is_authenitated if lookup fails
661 662 present in database
662 663
663 664 :param auth_user: instance of user to set attributes
664 665 :param user_id: user id to fetch by
665 666 :param api_key: api key to fetch by
666 667 :param username: username to fetch by
667 668 """
668 669 def token_obfuscate(token):
669 670 if token:
670 671 return token[:4] + "****"
671 672
672 673 if user_id is None and api_key is None and username is None:
673 674 raise Exception('You need to pass user_id, api_key or username')
674 675
675 676 log.debug(
676 677 'AuthUser: fill data execution based on: '
677 678 'user_id:%s api_key:%s username:%s', user_id, api_key, username)
678 679 try:
679 680 dbuser = None
680 681 if user_id:
681 682 dbuser = self.get(user_id)
682 683 elif api_key:
683 684 dbuser = self.get_by_auth_token(api_key)
684 685 elif username:
685 686 dbuser = self.get_by_username(username)
686 687
687 688 if not dbuser:
688 689 log.warning(
689 690 'Unable to lookup user by id:%s api_key:%s username:%s',
690 691 user_id, token_obfuscate(api_key), username)
691 692 return False
692 693 if not dbuser.active:
693 694 log.debug('User `%s:%s` is inactive, skipping fill data',
694 695 username, user_id)
695 696 return False
696 697
697 698 log.debug('AuthUser: filling found user:%s data', dbuser)
698 699 user_data = dbuser.get_dict()
699 700
700 701 user_data.update({
701 702 # set explicit the safe escaped values
702 703 'first_name': dbuser.first_name,
703 704 'last_name': dbuser.last_name,
704 705 })
705 706
706 707 for k, v in user_data.items():
707 708 # properties of auth user we dont update
708 709 if k not in ['auth_tokens', 'permissions']:
709 710 setattr(auth_user, k, v)
710 711
711 712 except Exception:
712 713 log.error(traceback.format_exc())
713 714 auth_user.is_authenticated = False
714 715 return False
715 716
716 717 return True
717 718
718 719 def has_perm(self, user, perm):
719 720 perm = self._get_perm(perm)
720 721 user = self._get_user(user)
721 722
722 723 return UserToPerm.query().filter(UserToPerm.user == user)\
723 724 .filter(UserToPerm.permission == perm).scalar() is not None
724 725
725 726 def grant_perm(self, user, perm):
726 727 """
727 728 Grant user global permissions
728 729
729 730 :param user:
730 731 :param perm:
731 732 """
732 733 user = self._get_user(user)
733 734 perm = self._get_perm(perm)
734 735 # if this permission is already granted skip it
735 736 _perm = UserToPerm.query()\
736 737 .filter(UserToPerm.user == user)\
737 738 .filter(UserToPerm.permission == perm)\
738 739 .scalar()
739 740 if _perm:
740 741 return
741 742 new = UserToPerm()
742 743 new.user = user
743 744 new.permission = perm
744 745 self.sa.add(new)
745 746 return new
746 747
747 748 def revoke_perm(self, user, perm):
748 749 """
749 750 Revoke users global permissions
750 751
751 752 :param user:
752 753 :param perm:
753 754 """
754 755 user = self._get_user(user)
755 756 perm = self._get_perm(perm)
756 757
757 758 obj = UserToPerm.query()\
758 759 .filter(UserToPerm.user == user)\
759 760 .filter(UserToPerm.permission == perm)\
760 761 .scalar()
761 762 if obj:
762 763 self.sa.delete(obj)
763 764
764 765 def add_extra_email(self, user, email):
765 766 """
766 767 Adds email address to UserEmailMap
767 768
768 769 :param user:
769 770 :param email:
770 771 """
771 772
772 773 user = self._get_user(user)
773 774
774 775 obj = UserEmailMap()
775 776 obj.user = user
776 777 obj.email = email
777 778 self.sa.add(obj)
778 779 return obj
779 780
780 781 def delete_extra_email(self, user, email_id):
781 782 """
782 783 Removes email address from UserEmailMap
783 784
784 785 :param user:
785 786 :param email_id:
786 787 """
787 788 user = self._get_user(user)
788 789 obj = UserEmailMap.query().get(email_id)
789 790 if obj and obj.user_id == user.user_id:
790 791 self.sa.delete(obj)
791 792
792 793 def parse_ip_range(self, ip_range):
793 794 ip_list = []
794 795
795 796 def make_unique(value):
796 797 seen = []
797 798 return [c for c in value if not (c in seen or seen.append(c))]
798 799
799 800 # firsts split by commas
800 801 for ip_range in ip_range.split(','):
801 802 if not ip_range:
802 803 continue
803 804 ip_range = ip_range.strip()
804 805 if '-' in ip_range:
805 806 start_ip, end_ip = ip_range.split('-', 1)
806 807 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
807 808 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
808 809 parsed_ip_range = []
809 810
810 811 for index in xrange(int(start_ip), int(end_ip) + 1):
811 812 new_ip = ipaddress.ip_address(index)
812 813 parsed_ip_range.append(str(new_ip))
813 814 ip_list.extend(parsed_ip_range)
814 815 else:
815 816 ip_list.append(ip_range)
816 817
817 818 return make_unique(ip_list)
818 819
819 820 def add_extra_ip(self, user, ip, description=None):
820 821 """
821 822 Adds ip address to UserIpMap
822 823
823 824 :param user:
824 825 :param ip:
825 826 """
826 827
827 828 user = self._get_user(user)
828 829 obj = UserIpMap()
829 830 obj.user = user
830 831 obj.ip_addr = ip
831 832 obj.description = description
832 833 self.sa.add(obj)
833 834 return obj
834 835
835 836 auth_token_role = AuthTokenModel.cls
836 837
837 838 def add_auth_token(self, user, lifetime_minutes, role, description=u'',
838 839 scope_callback=None):
839 840 """
840 841 Add AuthToken for user.
841 842
842 843 :param user: username/user_id
843 844 :param lifetime_minutes: in minutes the lifetime for token, -1 equals no limit
844 845 :param role: one of AuthTokenModel.cls.ROLE_*
845 846 :param description: optional string description
846 847 """
847 848
848 849 token = AuthTokenModel().create(
849 850 user, description, lifetime_minutes, role)
850 851 if scope_callback and callable(scope_callback):
851 852 # call the callback if we provide, used to attach scope for EE edition
852 853 scope_callback(token)
853 854 return token
854 855
855 856 def delete_extra_ip(self, user, ip_id):
856 857 """
857 858 Removes ip address from UserIpMap
858 859
859 860 :param user:
860 861 :param ip_id:
861 862 """
862 863 user = self._get_user(user)
863 864 obj = UserIpMap.query().get(ip_id)
864 865 if obj and obj.user_id == user.user_id:
865 866 self.sa.delete(obj)
866 867
867 868 def get_accounts_in_creation_order(self, current_user=None):
868 869 """
869 870 Get accounts in order of creation for deactivation for license limits
870 871
871 872 pick currently logged in user, and append to the list in position 0
872 873 pick all super-admins in order of creation date and add it to the list
873 874 pick all other accounts in order of creation and add it to the list.
874 875
875 876 Based on that list, the last accounts can be disabled as they are
876 877 created at the end and don't include any of the super admins as well
877 878 as the current user.
878 879
879 880 :param current_user: optionally current user running this operation
880 881 """
881 882
882 883 if not current_user:
883 884 current_user = get_current_rhodecode_user()
884 885 active_super_admins = [
885 886 x.user_id for x in User.query()
886 887 .filter(User.user_id != current_user.user_id)
887 888 .filter(User.active == true())
888 889 .filter(User.admin == true())
889 890 .order_by(User.created_on.asc())]
890 891
891 892 active_regular_users = [
892 893 x.user_id for x in User.query()
893 894 .filter(User.user_id != current_user.user_id)
894 895 .filter(User.active == true())
895 896 .filter(User.admin == false())
896 897 .order_by(User.created_on.asc())]
897 898
898 899 list_of_accounts = [current_user.user_id]
899 900 list_of_accounts += active_super_admins
900 901 list_of_accounts += active_regular_users
901 902
902 903 return list_of_accounts
903 904
904 905 def deactivate_last_users(self, expected_users, current_user=None):
905 906 """
906 907 Deactivate accounts that are over the license limits.
907 908 Algorithm of which accounts to disabled is based on the formula:
908 909
909 910 Get current user, then super admins in creation order, then regular
910 911 active users in creation order.
911 912
912 913 Using that list we mark all accounts from the end of it as inactive.
913 914 This way we block only latest created accounts.
914 915
915 916 :param expected_users: list of users in special order, we deactivate
916 917 the end N amount of users from that list
917 918 """
918 919
919 920 list_of_accounts = self.get_accounts_in_creation_order(
920 921 current_user=current_user)
921 922
922 923 for acc_id in list_of_accounts[expected_users + 1:]:
923 924 user = User.get(acc_id)
924 925 log.info('Deactivating account %s for license unlock', user)
925 926 user.active = False
926 927 Session().add(user)
927 928 Session().commit()
928 929
929 930 return
930 931
931 932 def get_user_log(self, user, filter_term):
932 933 user_log = UserLog.query()\
933 934 .filter(or_(UserLog.user_id == user.user_id,
934 935 UserLog.username == user.username))\
935 936 .options(joinedload(UserLog.user))\
936 937 .options(joinedload(UserLog.repository))\
937 938 .order_by(UserLog.action_date.desc())
938 939
939 940 user_log = user_log_filter(user_log, filter_term)
940 941 return user_log
General Comments 0
You need to be logged in to leave comments. Login now