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