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