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