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