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