##// END OF EJS Templates
feed-token, user, performance: lazy load the feed_token. We only need it for...
marcink -
r2424:76f7c7cb default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,2187 +1,2190 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 authentication and permission libraries
23 23 """
24 24
25 25 import os
26 26 import inspect
27 27 import collections
28 28 import fnmatch
29 29 import hashlib
30 30 import itertools
31 31 import logging
32 32 import random
33 33 import traceback
34 34 from functools import wraps
35 35
36 36 import ipaddress
37 37
38 38 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
39 39 from sqlalchemy.orm.exc import ObjectDeletedError
40 40 from sqlalchemy.orm import joinedload
41 41 from zope.cachedescriptors.property import Lazy as LazyProperty
42 42
43 43 import rhodecode
44 44 from rhodecode.model import meta
45 45 from rhodecode.model.meta import Session
46 46 from rhodecode.model.user import UserModel
47 47 from rhodecode.model.db import (
48 48 User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
49 49 UserIpMap, UserApiKeys, RepoGroup, UserGroup)
50 50 from rhodecode.lib import caches
51 51 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5
52 52 from rhodecode.lib.utils import (
53 53 get_repo_slug, get_repo_group_slug, get_user_group_slug)
54 54 from rhodecode.lib.caching_query import FromCache
55 55
56 56
57 57 if rhodecode.is_unix:
58 58 import bcrypt
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62 csrf_token_key = "csrf_token"
63 63
64 64
65 65 class PasswordGenerator(object):
66 66 """
67 67 This is a simple class for generating password from different sets of
68 68 characters
69 69 usage::
70 70
71 71 passwd_gen = PasswordGenerator()
72 72 #print 8-letter password containing only big and small letters
73 73 of alphabet
74 74 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
75 75 """
76 76 ALPHABETS_NUM = r'''1234567890'''
77 77 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
78 78 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
79 79 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
80 80 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
81 81 + ALPHABETS_NUM + ALPHABETS_SPECIAL
82 82 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
83 83 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
84 84 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
85 85 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
86 86
87 87 def __init__(self, passwd=''):
88 88 self.passwd = passwd
89 89
90 90 def gen_password(self, length, type_=None):
91 91 if type_ is None:
92 92 type_ = self.ALPHABETS_FULL
93 93 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
94 94 return self.passwd
95 95
96 96
97 97 class _RhodeCodeCryptoBase(object):
98 98 ENC_PREF = None
99 99
100 100 def hash_create(self, str_):
101 101 """
102 102 hash the string using
103 103
104 104 :param str_: password to hash
105 105 """
106 106 raise NotImplementedError
107 107
108 108 def hash_check_with_upgrade(self, password, hashed):
109 109 """
110 110 Returns tuple in which first element is boolean that states that
111 111 given password matches it's hashed version, and the second is new hash
112 112 of the password, in case this password should be migrated to new
113 113 cipher.
114 114 """
115 115 checked_hash = self.hash_check(password, hashed)
116 116 return checked_hash, None
117 117
118 118 def hash_check(self, password, hashed):
119 119 """
120 120 Checks matching password with it's hashed value.
121 121
122 122 :param password: password
123 123 :param hashed: password in hashed form
124 124 """
125 125 raise NotImplementedError
126 126
127 127 def _assert_bytes(self, value):
128 128 """
129 129 Passing in an `unicode` object can lead to hard to detect issues
130 130 if passwords contain non-ascii characters. Doing a type check
131 131 during runtime, so that such mistakes are detected early on.
132 132 """
133 133 if not isinstance(value, str):
134 134 raise TypeError(
135 135 "Bytestring required as input, got %r." % (value, ))
136 136
137 137
138 138 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
139 139 ENC_PREF = ('$2a$10', '$2b$10')
140 140
141 141 def hash_create(self, str_):
142 142 self._assert_bytes(str_)
143 143 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
144 144
145 145 def hash_check_with_upgrade(self, password, hashed):
146 146 """
147 147 Returns tuple in which first element is boolean that states that
148 148 given password matches it's hashed version, and the second is new hash
149 149 of the password, in case this password should be migrated to new
150 150 cipher.
151 151
152 152 This implements special upgrade logic which works like that:
153 153 - check if the given password == bcrypted hash, if yes then we
154 154 properly used password and it was already in bcrypt. Proceed
155 155 without any changes
156 156 - if bcrypt hash check is not working try with sha256. If hash compare
157 157 is ok, it means we using correct but old hashed password. indicate
158 158 hash change and proceed
159 159 """
160 160
161 161 new_hash = None
162 162
163 163 # regular pw check
164 164 password_match_bcrypt = self.hash_check(password, hashed)
165 165
166 166 # now we want to know if the password was maybe from sha256
167 167 # basically calling _RhodeCodeCryptoSha256().hash_check()
168 168 if not password_match_bcrypt:
169 169 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
170 170 new_hash = self.hash_create(password) # make new bcrypt hash
171 171 password_match_bcrypt = True
172 172
173 173 return password_match_bcrypt, new_hash
174 174
175 175 def hash_check(self, password, hashed):
176 176 """
177 177 Checks matching password with it's hashed value.
178 178
179 179 :param password: password
180 180 :param hashed: password in hashed form
181 181 """
182 182 self._assert_bytes(password)
183 183 try:
184 184 return bcrypt.hashpw(password, hashed) == hashed
185 185 except ValueError as e:
186 186 # we're having a invalid salt here probably, we should not crash
187 187 # just return with False as it would be a wrong password.
188 188 log.debug('Failed to check password hash using bcrypt %s',
189 189 safe_str(e))
190 190
191 191 return False
192 192
193 193
194 194 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
195 195 ENC_PREF = '_'
196 196
197 197 def hash_create(self, str_):
198 198 self._assert_bytes(str_)
199 199 return hashlib.sha256(str_).hexdigest()
200 200
201 201 def hash_check(self, password, hashed):
202 202 """
203 203 Checks matching password with it's hashed value.
204 204
205 205 :param password: password
206 206 :param hashed: password in hashed form
207 207 """
208 208 self._assert_bytes(password)
209 209 return hashlib.sha256(password).hexdigest() == hashed
210 210
211 211
212 212 class _RhodeCodeCryptoMd5(_RhodeCodeCryptoBase):
213 213 ENC_PREF = '_'
214 214
215 215 def hash_create(self, str_):
216 216 self._assert_bytes(str_)
217 217 return hashlib.md5(str_).hexdigest()
218 218
219 219 def hash_check(self, password, hashed):
220 220 """
221 221 Checks matching password with it's hashed value.
222 222
223 223 :param password: password
224 224 :param hashed: password in hashed form
225 225 """
226 226 self._assert_bytes(password)
227 227 return hashlib.md5(password).hexdigest() == hashed
228 228
229 229
230 230 def crypto_backend():
231 231 """
232 232 Return the matching crypto backend.
233 233
234 234 Selection is based on if we run tests or not, we pick md5 backend to run
235 235 tests faster since BCRYPT is expensive to calculate
236 236 """
237 237 if rhodecode.is_test:
238 238 RhodeCodeCrypto = _RhodeCodeCryptoMd5()
239 239 else:
240 240 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
241 241
242 242 return RhodeCodeCrypto
243 243
244 244
245 245 def get_crypt_password(password):
246 246 """
247 247 Create the hash of `password` with the active crypto backend.
248 248
249 249 :param password: The cleartext password.
250 250 :type password: unicode
251 251 """
252 252 password = safe_str(password)
253 253 return crypto_backend().hash_create(password)
254 254
255 255
256 256 def check_password(password, hashed):
257 257 """
258 258 Check if the value in `password` matches the hash in `hashed`.
259 259
260 260 :param password: The cleartext password.
261 261 :type password: unicode
262 262
263 263 :param hashed: The expected hashed version of the password.
264 264 :type hashed: The hash has to be passed in in text representation.
265 265 """
266 266 password = safe_str(password)
267 267 return crypto_backend().hash_check(password, hashed)
268 268
269 269
270 270 def generate_auth_token(data, salt=None):
271 271 """
272 272 Generates API KEY from given string
273 273 """
274 274
275 275 if salt is None:
276 276 salt = os.urandom(16)
277 277 return hashlib.sha1(safe_str(data) + salt).hexdigest()
278 278
279 279
280 280 def get_came_from(request):
281 281 """
282 282 get query_string+path from request sanitized after removing auth_token
283 283 """
284 284 _req = request
285 285
286 286 path = _req.path
287 287 if 'auth_token' in _req.GET:
288 288 # sanitize the request and remove auth_token for redirection
289 289 _req.GET.pop('auth_token')
290 290 qs = _req.query_string
291 291 if qs:
292 292 path += '?' + qs
293 293
294 294 return path
295 295
296 296
297 297 class CookieStoreWrapper(object):
298 298
299 299 def __init__(self, cookie_store):
300 300 self.cookie_store = cookie_store
301 301
302 302 def __repr__(self):
303 303 return 'CookieStore<%s>' % (self.cookie_store)
304 304
305 305 def get(self, key, other=None):
306 306 if isinstance(self.cookie_store, dict):
307 307 return self.cookie_store.get(key, other)
308 308 elif isinstance(self.cookie_store, AuthUser):
309 309 return self.cookie_store.__dict__.get(key, other)
310 310
311 311
312 312 def _cached_perms_data(user_id, scope, user_is_admin,
313 313 user_inherit_default_permissions, explicit, algo,
314 314 calculate_super_admin):
315 315
316 316 permissions = PermissionCalculator(
317 317 user_id, scope, user_is_admin, user_inherit_default_permissions,
318 318 explicit, algo, calculate_super_admin)
319 319 return permissions.calculate()
320 320
321 321
322 322 class PermOrigin(object):
323 323 SUPER_ADMIN = 'superadmin'
324 324
325 325 REPO_USER = 'user:%s'
326 326 REPO_USERGROUP = 'usergroup:%s'
327 327 REPO_OWNER = 'repo.owner'
328 328 REPO_DEFAULT = 'repo.default'
329 329 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
330 330 REPO_PRIVATE = 'repo.private'
331 331
332 332 REPOGROUP_USER = 'user:%s'
333 333 REPOGROUP_USERGROUP = 'usergroup:%s'
334 334 REPOGROUP_OWNER = 'group.owner'
335 335 REPOGROUP_DEFAULT = 'group.default'
336 336 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
337 337
338 338 USERGROUP_USER = 'user:%s'
339 339 USERGROUP_USERGROUP = 'usergroup:%s'
340 340 USERGROUP_OWNER = 'usergroup.owner'
341 341 USERGROUP_DEFAULT = 'usergroup.default'
342 342 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
343 343
344 344
345 345 class PermOriginDict(dict):
346 346 """
347 347 A special dict used for tracking permissions along with their origins.
348 348
349 349 `__setitem__` has been overridden to expect a tuple(perm, origin)
350 350 `__getitem__` will return only the perm
351 351 `.perm_origin_stack` will return the stack of (perm, origin) set per key
352 352
353 353 >>> perms = PermOriginDict()
354 354 >>> perms['resource'] = 'read', 'default'
355 355 >>> perms['resource']
356 356 'read'
357 357 >>> perms['resource'] = 'write', 'admin'
358 358 >>> perms['resource']
359 359 'write'
360 360 >>> perms.perm_origin_stack
361 361 {'resource': [('read', 'default'), ('write', 'admin')]}
362 362 """
363 363
364 364 def __init__(self, *args, **kw):
365 365 dict.__init__(self, *args, **kw)
366 366 self.perm_origin_stack = collections.OrderedDict()
367 367
368 368 def __setitem__(self, key, (perm, origin)):
369 369 self.perm_origin_stack.setdefault(key, []).append((perm, origin))
370 370 dict.__setitem__(self, key, perm)
371 371
372 372
373 373 class PermissionCalculator(object):
374 374
375 375 def __init__(
376 376 self, user_id, scope, user_is_admin,
377 377 user_inherit_default_permissions, explicit, algo,
378 378 calculate_super_admin=False):
379 379
380 380 self.user_id = user_id
381 381 self.user_is_admin = user_is_admin
382 382 self.inherit_default_permissions = user_inherit_default_permissions
383 383 self.explicit = explicit
384 384 self.algo = algo
385 385 self.calculate_super_admin = calculate_super_admin
386 386
387 387 scope = scope or {}
388 388 self.scope_repo_id = scope.get('repo_id')
389 389 self.scope_repo_group_id = scope.get('repo_group_id')
390 390 self.scope_user_group_id = scope.get('user_group_id')
391 391
392 392 self.default_user_id = User.get_default_user(cache=True).user_id
393 393
394 394 self.permissions_repositories = PermOriginDict()
395 395 self.permissions_repository_groups = PermOriginDict()
396 396 self.permissions_user_groups = PermOriginDict()
397 397 self.permissions_global = set()
398 398
399 399 self.default_repo_perms = Permission.get_default_repo_perms(
400 400 self.default_user_id, self.scope_repo_id)
401 401 self.default_repo_groups_perms = Permission.get_default_group_perms(
402 402 self.default_user_id, self.scope_repo_group_id)
403 403 self.default_user_group_perms = \
404 404 Permission.get_default_user_group_perms(
405 405 self.default_user_id, self.scope_user_group_id)
406 406
407 407 def calculate(self):
408 408 if self.user_is_admin and not self.calculate_super_admin:
409 409 return self._admin_permissions()
410 410
411 411 self._calculate_global_default_permissions()
412 412 self._calculate_global_permissions()
413 413 self._calculate_default_permissions()
414 414 self._calculate_repository_permissions()
415 415 self._calculate_repository_group_permissions()
416 416 self._calculate_user_group_permissions()
417 417 return self._permission_structure()
418 418
419 419 def _admin_permissions(self):
420 420 """
421 421 admin user have all default rights for repositories
422 422 and groups set to admin
423 423 """
424 424 self.permissions_global.add('hg.admin')
425 425 self.permissions_global.add('hg.create.write_on_repogroup.true')
426 426
427 427 # repositories
428 428 for perm in self.default_repo_perms:
429 429 r_k = perm.UserRepoToPerm.repository.repo_name
430 430 p = 'repository.admin'
431 431 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN
432 432
433 433 # repository groups
434 434 for perm in self.default_repo_groups_perms:
435 435 rg_k = perm.UserRepoGroupToPerm.group.group_name
436 436 p = 'group.admin'
437 437 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN
438 438
439 439 # user groups
440 440 for perm in self.default_user_group_perms:
441 441 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
442 442 p = 'usergroup.admin'
443 443 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
444 444
445 445 return self._permission_structure()
446 446
447 447 def _calculate_global_default_permissions(self):
448 448 """
449 449 global permissions taken from the default user
450 450 """
451 451 default_global_perms = UserToPerm.query()\
452 452 .filter(UserToPerm.user_id == self.default_user_id)\
453 453 .options(joinedload(UserToPerm.permission))
454 454
455 455 for perm in default_global_perms:
456 456 self.permissions_global.add(perm.permission.permission_name)
457 457
458 458 if self.user_is_admin:
459 459 self.permissions_global.add('hg.admin')
460 460 self.permissions_global.add('hg.create.write_on_repogroup.true')
461 461
462 462 def _calculate_global_permissions(self):
463 463 """
464 464 Set global system permissions with user permissions or permissions
465 465 taken from the user groups of the current user.
466 466
467 467 The permissions include repo creating, repo group creating, forking
468 468 etc.
469 469 """
470 470
471 471 # now we read the defined permissions and overwrite what we have set
472 472 # before those can be configured from groups or users explicitly.
473 473
474 474 # TODO: johbo: This seems to be out of sync, find out the reason
475 475 # for the comment below and update it.
476 476
477 477 # In case we want to extend this list we should be always in sync with
478 478 # User.DEFAULT_USER_PERMISSIONS definitions
479 479 _configurable = frozenset([
480 480 'hg.fork.none', 'hg.fork.repository',
481 481 'hg.create.none', 'hg.create.repository',
482 482 'hg.usergroup.create.false', 'hg.usergroup.create.true',
483 483 'hg.repogroup.create.false', 'hg.repogroup.create.true',
484 484 'hg.create.write_on_repogroup.false',
485 485 'hg.create.write_on_repogroup.true',
486 486 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
487 487 ])
488 488
489 489 # USER GROUPS comes first user group global permissions
490 490 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
491 491 .options(joinedload(UserGroupToPerm.permission))\
492 492 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
493 493 UserGroupMember.users_group_id))\
494 494 .filter(UserGroupMember.user_id == self.user_id)\
495 495 .order_by(UserGroupToPerm.users_group_id)\
496 496 .all()
497 497
498 498 # need to group here by groups since user can be in more than
499 499 # one group, so we get all groups
500 500 _explicit_grouped_perms = [
501 501 [x, list(y)] for x, y in
502 502 itertools.groupby(user_perms_from_users_groups,
503 503 lambda _x: _x.users_group)]
504 504
505 505 for gr, perms in _explicit_grouped_perms:
506 506 # since user can be in multiple groups iterate over them and
507 507 # select the lowest permissions first (more explicit)
508 508 # TODO: marcink: do this^^
509 509
510 510 # group doesn't inherit default permissions so we actually set them
511 511 if not gr.inherit_default_permissions:
512 512 # NEED TO IGNORE all previously set configurable permissions
513 513 # and replace them with explicitly set from this user
514 514 # group permissions
515 515 self.permissions_global = self.permissions_global.difference(
516 516 _configurable)
517 517 for perm in perms:
518 518 self.permissions_global.add(perm.permission.permission_name)
519 519
520 520 # user explicit global permissions
521 521 user_perms = Session().query(UserToPerm)\
522 522 .options(joinedload(UserToPerm.permission))\
523 523 .filter(UserToPerm.user_id == self.user_id).all()
524 524
525 525 if not self.inherit_default_permissions:
526 526 # NEED TO IGNORE all configurable permissions and
527 527 # replace them with explicitly set from this user permissions
528 528 self.permissions_global = self.permissions_global.difference(
529 529 _configurable)
530 530 for perm in user_perms:
531 531 self.permissions_global.add(perm.permission.permission_name)
532 532
533 533 def _calculate_default_permissions(self):
534 534 """
535 535 Set default user permissions for repositories, repository groups
536 536 taken from the default user.
537 537
538 538 Calculate inheritance of object permissions based on what we have now
539 539 in GLOBAL permissions. We check if .false is in GLOBAL since this is
540 540 explicitly set. Inherit is the opposite of .false being there.
541 541
542 542 .. note::
543 543
544 544 the syntax is little bit odd but what we need to check here is
545 545 the opposite of .false permission being in the list so even for
546 546 inconsistent state when both .true/.false is there
547 547 .false is more important
548 548
549 549 """
550 550 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
551 551 in self.permissions_global)
552 552
553 553 # defaults for repositories, taken from `default` user permissions
554 554 # on given repo
555 555 for perm in self.default_repo_perms:
556 556 r_k = perm.UserRepoToPerm.repository.repo_name
557 557 p = perm.Permission.permission_name
558 558 o = PermOrigin.REPO_DEFAULT
559 559 self.permissions_repositories[r_k] = p, o
560 560
561 561 # if we decide this user isn't inheriting permissions from
562 562 # default user we set him to .none so only explicit
563 563 # permissions work
564 564 if not user_inherit_object_permissions:
565 565 p = 'repository.none'
566 566 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
567 567 self.permissions_repositories[r_k] = p, o
568 568
569 569 if perm.Repository.private and not (
570 570 perm.Repository.user_id == self.user_id):
571 571 # disable defaults for private repos,
572 572 p = 'repository.none'
573 573 o = PermOrigin.REPO_PRIVATE
574 574 self.permissions_repositories[r_k] = p, o
575 575
576 576 elif perm.Repository.user_id == self.user_id:
577 577 # set admin if owner
578 578 p = 'repository.admin'
579 579 o = PermOrigin.REPO_OWNER
580 580 self.permissions_repositories[r_k] = p, o
581 581
582 582 if self.user_is_admin:
583 583 p = 'repository.admin'
584 584 o = PermOrigin.SUPER_ADMIN
585 585 self.permissions_repositories[r_k] = p, o
586 586
587 587 # defaults for repository groups taken from `default` user permission
588 588 # on given group
589 589 for perm in self.default_repo_groups_perms:
590 590 rg_k = perm.UserRepoGroupToPerm.group.group_name
591 591 p = perm.Permission.permission_name
592 592 o = PermOrigin.REPOGROUP_DEFAULT
593 593 self.permissions_repository_groups[rg_k] = p, o
594 594
595 595 # if we decide this user isn't inheriting permissions from default
596 596 # user we set him to .none so only explicit permissions work
597 597 if not user_inherit_object_permissions:
598 598 p = 'group.none'
599 599 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
600 600 self.permissions_repository_groups[rg_k] = p, o
601 601
602 602 if perm.RepoGroup.user_id == self.user_id:
603 603 # set admin if owner
604 604 p = 'group.admin'
605 605 o = PermOrigin.REPOGROUP_OWNER
606 606 self.permissions_repository_groups[rg_k] = p, o
607 607
608 608 if self.user_is_admin:
609 609 p = 'group.admin'
610 610 o = PermOrigin.SUPER_ADMIN
611 611 self.permissions_repository_groups[rg_k] = p, o
612 612
613 613 # defaults for user groups taken from `default` user permission
614 614 # on given user group
615 615 for perm in self.default_user_group_perms:
616 616 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
617 617 p = perm.Permission.permission_name
618 618 o = PermOrigin.USERGROUP_DEFAULT
619 619 self.permissions_user_groups[u_k] = p, o
620 620
621 621 # if we decide this user isn't inheriting permissions from default
622 622 # user we set him to .none so only explicit permissions work
623 623 if not user_inherit_object_permissions:
624 624 p = 'usergroup.none'
625 625 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
626 626 self.permissions_user_groups[u_k] = p, o
627 627
628 628 if perm.UserGroup.user_id == self.user_id:
629 629 # set admin if owner
630 630 p = 'usergroup.admin'
631 631 o = PermOrigin.USERGROUP_OWNER
632 632 self.permissions_user_groups[u_k] = p, o
633 633
634 634 if self.user_is_admin:
635 635 p = 'usergroup.admin'
636 636 o = PermOrigin.SUPER_ADMIN
637 637 self.permissions_user_groups[u_k] = p, o
638 638
639 639 def _calculate_repository_permissions(self):
640 640 """
641 641 Repository permissions for the current user.
642 642
643 643 Check if the user is part of user groups for this repository and
644 644 fill in the permission from it. `_choose_permission` decides of which
645 645 permission should be selected based on selected method.
646 646 """
647 647
648 648 # user group for repositories permissions
649 649 user_repo_perms_from_user_group = Permission\
650 650 .get_default_repo_perms_from_user_group(
651 651 self.user_id, self.scope_repo_id)
652 652
653 653 multiple_counter = collections.defaultdict(int)
654 654 for perm in user_repo_perms_from_user_group:
655 655 r_k = perm.UserGroupRepoToPerm.repository.repo_name
656 656 multiple_counter[r_k] += 1
657 657 p = perm.Permission.permission_name
658 658 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
659 659 .users_group.users_group_name
660 660
661 661 if multiple_counter[r_k] > 1:
662 662 cur_perm = self.permissions_repositories[r_k]
663 663 p = self._choose_permission(p, cur_perm)
664 664
665 665 self.permissions_repositories[r_k] = p, o
666 666
667 667 if perm.Repository.user_id == self.user_id:
668 668 # set admin if owner
669 669 p = 'repository.admin'
670 670 o = PermOrigin.REPO_OWNER
671 671 self.permissions_repositories[r_k] = p, o
672 672
673 673 if self.user_is_admin:
674 674 p = 'repository.admin'
675 675 o = PermOrigin.SUPER_ADMIN
676 676 self.permissions_repositories[r_k] = p, o
677 677
678 678 # user explicit permissions for repositories, overrides any specified
679 679 # by the group permission
680 680 user_repo_perms = Permission.get_default_repo_perms(
681 681 self.user_id, self.scope_repo_id)
682 682 for perm in user_repo_perms:
683 683 r_k = perm.UserRepoToPerm.repository.repo_name
684 684 p = perm.Permission.permission_name
685 685 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
686 686
687 687 if not self.explicit:
688 688 cur_perm = self.permissions_repositories.get(
689 689 r_k, 'repository.none')
690 690 p = self._choose_permission(p, cur_perm)
691 691
692 692 self.permissions_repositories[r_k] = p, o
693 693
694 694 if perm.Repository.user_id == self.user_id:
695 695 # set admin if owner
696 696 p = 'repository.admin'
697 697 o = PermOrigin.REPO_OWNER
698 698 self.permissions_repositories[r_k] = p, o
699 699
700 700 if self.user_is_admin:
701 701 p = 'repository.admin'
702 702 o = PermOrigin.SUPER_ADMIN
703 703 self.permissions_repositories[r_k] = p, o
704 704
705 705 def _calculate_repository_group_permissions(self):
706 706 """
707 707 Repository group permissions for the current user.
708 708
709 709 Check if the user is part of user groups for repository groups and
710 710 fill in the permissions from it. `_choose_permission` decides of which
711 711 permission should be selected based on selected method.
712 712 """
713 713 # user group for repo groups permissions
714 714 user_repo_group_perms_from_user_group = Permission\
715 715 .get_default_group_perms_from_user_group(
716 716 self.user_id, self.scope_repo_group_id)
717 717
718 718 multiple_counter = collections.defaultdict(int)
719 719 for perm in user_repo_group_perms_from_user_group:
720 720 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
721 721 multiple_counter[rg_k] += 1
722 722 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
723 723 .users_group.users_group_name
724 724 p = perm.Permission.permission_name
725 725
726 726 if multiple_counter[rg_k] > 1:
727 727 cur_perm = self.permissions_repository_groups[rg_k]
728 728 p = self._choose_permission(p, cur_perm)
729 729 self.permissions_repository_groups[rg_k] = p, o
730 730
731 731 if perm.RepoGroup.user_id == self.user_id:
732 732 # set admin if owner, even for member of other user group
733 733 p = 'group.admin'
734 734 o = PermOrigin.REPOGROUP_OWNER
735 735 self.permissions_repository_groups[rg_k] = p, o
736 736
737 737 if self.user_is_admin:
738 738 p = 'group.admin'
739 739 o = PermOrigin.SUPER_ADMIN
740 740 self.permissions_repository_groups[rg_k] = p, o
741 741
742 742 # user explicit permissions for repository groups
743 743 user_repo_groups_perms = Permission.get_default_group_perms(
744 744 self.user_id, self.scope_repo_group_id)
745 745 for perm in user_repo_groups_perms:
746 746 rg_k = perm.UserRepoGroupToPerm.group.group_name
747 747 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
748 748 .user.username
749 749 p = perm.Permission.permission_name
750 750
751 751 if not self.explicit:
752 752 cur_perm = self.permissions_repository_groups.get(
753 753 rg_k, 'group.none')
754 754 p = self._choose_permission(p, cur_perm)
755 755
756 756 self.permissions_repository_groups[rg_k] = p, o
757 757
758 758 if perm.RepoGroup.user_id == self.user_id:
759 759 # set admin if owner
760 760 p = 'group.admin'
761 761 o = PermOrigin.REPOGROUP_OWNER
762 762 self.permissions_repository_groups[rg_k] = p, o
763 763
764 764 if self.user_is_admin:
765 765 p = 'group.admin'
766 766 o = PermOrigin.SUPER_ADMIN
767 767 self.permissions_repository_groups[rg_k] = p, o
768 768
769 769 def _calculate_user_group_permissions(self):
770 770 """
771 771 User group permissions for the current user.
772 772 """
773 773 # user group for user group permissions
774 774 user_group_from_user_group = Permission\
775 775 .get_default_user_group_perms_from_user_group(
776 776 self.user_id, self.scope_user_group_id)
777 777
778 778 multiple_counter = collections.defaultdict(int)
779 779 for perm in user_group_from_user_group:
780 780 ug_k = perm.UserGroupUserGroupToPerm\
781 781 .target_user_group.users_group_name
782 782 multiple_counter[ug_k] += 1
783 783 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
784 784 .user_group.users_group_name
785 785 p = perm.Permission.permission_name
786 786
787 787 if multiple_counter[ug_k] > 1:
788 788 cur_perm = self.permissions_user_groups[ug_k]
789 789 p = self._choose_permission(p, cur_perm)
790 790
791 791 self.permissions_user_groups[ug_k] = p, o
792 792
793 793 if perm.UserGroup.user_id == self.user_id:
794 794 # set admin if owner, even for member of other user group
795 795 p = 'usergroup.admin'
796 796 o = PermOrigin.USERGROUP_OWNER
797 797 self.permissions_user_groups[ug_k] = p, o
798 798
799 799 if self.user_is_admin:
800 800 p = 'usergroup.admin'
801 801 o = PermOrigin.SUPER_ADMIN
802 802 self.permissions_user_groups[ug_k] = p, o
803 803
804 804 # user explicit permission for user groups
805 805 user_user_groups_perms = Permission.get_default_user_group_perms(
806 806 self.user_id, self.scope_user_group_id)
807 807 for perm in user_user_groups_perms:
808 808 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
809 809 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
810 810 .user.username
811 811 p = perm.Permission.permission_name
812 812
813 813 if not self.explicit:
814 814 cur_perm = self.permissions_user_groups.get(
815 815 ug_k, 'usergroup.none')
816 816 p = self._choose_permission(p, cur_perm)
817 817
818 818 self.permissions_user_groups[ug_k] = p, o
819 819
820 820 if perm.UserGroup.user_id == self.user_id:
821 821 # set admin if owner
822 822 p = 'usergroup.admin'
823 823 o = PermOrigin.USERGROUP_OWNER
824 824 self.permissions_user_groups[ug_k] = p, o
825 825
826 826 if self.user_is_admin:
827 827 p = 'usergroup.admin'
828 828 o = PermOrigin.SUPER_ADMIN
829 829 self.permissions_user_groups[ug_k] = p, o
830 830
831 831 def _choose_permission(self, new_perm, cur_perm):
832 832 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
833 833 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
834 834 if self.algo == 'higherwin':
835 835 if new_perm_val > cur_perm_val:
836 836 return new_perm
837 837 return cur_perm
838 838 elif self.algo == 'lowerwin':
839 839 if new_perm_val < cur_perm_val:
840 840 return new_perm
841 841 return cur_perm
842 842
843 843 def _permission_structure(self):
844 844 return {
845 845 'global': self.permissions_global,
846 846 'repositories': self.permissions_repositories,
847 847 'repositories_groups': self.permissions_repository_groups,
848 848 'user_groups': self.permissions_user_groups,
849 849 }
850 850
851 851
852 852 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
853 853 """
854 854 Check if given controller_name is in whitelist of auth token access
855 855 """
856 856 if not whitelist:
857 857 from rhodecode import CONFIG
858 858 whitelist = aslist(
859 859 CONFIG.get('api_access_controllers_whitelist'), sep=',')
860 860 # backward compat translation
861 861 compat = {
862 862 # old controller, new VIEW
863 863 'ChangesetController:*': 'RepoCommitsView:*',
864 864 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
865 865 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
866 866 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
867 867 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
868 868 'GistsController:*': 'GistView:*',
869 869 }
870 870
871 871 log.debug(
872 872 'Allowed views for AUTH TOKEN access: %s' % (whitelist,))
873 873 auth_token_access_valid = False
874 874
875 875 for entry in whitelist:
876 876 token_match = True
877 877 if entry in compat:
878 878 # translate from old Controllers to Pyramid Views
879 879 entry = compat[entry]
880 880
881 881 if '@' in entry:
882 882 # specific AuthToken
883 883 entry, allowed_token = entry.split('@', 1)
884 884 token_match = auth_token == allowed_token
885 885
886 886 if fnmatch.fnmatch(view_name, entry) and token_match:
887 887 auth_token_access_valid = True
888 888 break
889 889
890 890 if auth_token_access_valid:
891 891 log.debug('view: `%s` matches entry in whitelist: %s'
892 892 % (view_name, whitelist))
893 893 else:
894 894 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
895 895 % (view_name, whitelist))
896 896 if auth_token:
897 897 # if we use auth token key and don't have access it's a warning
898 898 log.warning(msg)
899 899 else:
900 900 log.debug(msg)
901 901
902 902 return auth_token_access_valid
903 903
904 904
905 905 class AuthUser(object):
906 906 """
907 907 A simple object that handles all attributes of user in RhodeCode
908 908
909 909 It does lookup based on API key,given user, or user present in session
910 910 Then it fills all required information for such user. It also checks if
911 911 anonymous access is enabled and if so, it returns default user as logged in
912 912 """
913 913 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
914 914
915 915 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
916 916
917 917 self.user_id = user_id
918 918 self._api_key = api_key
919 919
920 920 self.api_key = None
921 self.feed_token = ''
922 921 self.username = username
923 922 self.ip_addr = ip_addr
924 923 self.name = ''
925 924 self.lastname = ''
926 925 self.first_name = ''
927 926 self.last_name = ''
928 927 self.email = ''
929 928 self.is_authenticated = False
930 929 self.admin = False
931 930 self.inherit_default_permissions = False
932 931 self.password = ''
933 932
934 933 self.anonymous_user = None # propagated on propagate_data
935 934 self.propagate_data()
936 935 self._instance = None
937 936 self._permissions_scoped_cache = {} # used to bind scoped calculation
938 937
939 938 @LazyProperty
940 939 def permissions(self):
941 940 return self.get_perms(user=self, cache=False)
942 941
943 942 @LazyProperty
944 943 def permissions_safe(self):
945 944 """
946 945 Filtered permissions excluding not allowed repositories
947 946 """
948 947 perms = self.get_perms(user=self, cache=False)
949 948
950 949 perms['repositories'] = {
951 950 k: v for k, v in perms['repositories'].iteritems()
952 951 if v != 'repository.none'}
953 952 perms['repositories_groups'] = {
954 953 k: v for k, v in perms['repositories_groups'].iteritems()
955 954 if v != 'group.none'}
956 955 perms['user_groups'] = {
957 956 k: v for k, v in perms['user_groups'].iteritems()
958 957 if v != 'usergroup.none'}
959 958 return perms
960 959
961 960 @LazyProperty
962 961 def permissions_full_details(self):
963 962 return self.get_perms(
964 963 user=self, cache=False, calculate_super_admin=True)
965 964
966 965 def permissions_with_scope(self, scope):
967 966 """
968 967 Call the get_perms function with scoped data. The scope in that function
969 968 narrows the SQL calls to the given ID of objects resulting in fetching
970 969 Just particular permission we want to obtain. If scope is an empty dict
971 970 then it basically narrows the scope to GLOBAL permissions only.
972 971
973 972 :param scope: dict
974 973 """
975 974 if 'repo_name' in scope:
976 975 obj = Repository.get_by_repo_name(scope['repo_name'])
977 976 if obj:
978 977 scope['repo_id'] = obj.repo_id
979 978 _scope = {
980 979 'repo_id': -1,
981 980 'user_group_id': -1,
982 981 'repo_group_id': -1,
983 982 }
984 983 _scope.update(scope)
985 984 cache_key = "_".join(map(safe_str, reduce(lambda a, b: a+b,
986 985 _scope.items())))
987 986 if cache_key not in self._permissions_scoped_cache:
988 987 # store in cache to mimic how the @LazyProperty works,
989 988 # the difference here is that we use the unique key calculated
990 989 # from params and values
991 990 res = self.get_perms(user=self, cache=False, scope=_scope)
992 991 self._permissions_scoped_cache[cache_key] = res
993 992 return self._permissions_scoped_cache[cache_key]
994 993
995 994 def get_instance(self):
996 995 return User.get(self.user_id)
997 996
998 997 def update_lastactivity(self):
999 998 if self.user_id:
1000 999 User.get(self.user_id).update_lastactivity()
1001 1000
1002 1001 def propagate_data(self):
1003 1002 """
1004 1003 Fills in user data and propagates values to this instance. Maps fetched
1005 1004 user attributes to this class instance attributes
1006 1005 """
1007 1006 log.debug('AuthUser: starting data propagation for new potential user')
1008 1007 user_model = UserModel()
1009 1008 anon_user = self.anonymous_user = User.get_default_user(cache=True)
1010 1009 is_user_loaded = False
1011 1010
1012 1011 # lookup by userid
1013 1012 if self.user_id is not None and self.user_id != anon_user.user_id:
1014 1013 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1015 1014 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1016 1015
1017 1016 # try go get user by api key
1018 1017 elif self._api_key and self._api_key != anon_user.api_key:
1019 1018 log.debug('Trying Auth User lookup by API KEY: `%s`', self._api_key)
1020 1019 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1021 1020
1022 1021 # lookup by username
1023 1022 elif self.username:
1024 1023 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1025 1024 is_user_loaded = user_model.fill_data(self, username=self.username)
1026 1025 else:
1027 1026 log.debug('No data in %s that could been used to log in', self)
1028 1027
1029 1028 if not is_user_loaded:
1030 1029 log.debug(
1031 1030 'Failed to load user. Fallback to default user %s', anon_user)
1032 1031 # if we cannot authenticate user try anonymous
1033 1032 if anon_user.active:
1034 1033 log.debug('default user is active, using it as a session user')
1035 1034 user_model.fill_data(self, user_id=anon_user.user_id)
1036 1035 # then we set this user is logged in
1037 1036 self.is_authenticated = True
1038 1037 else:
1039 1038 log.debug('default user is NOT active')
1040 1039 # in case of disabled anonymous user we reset some of the
1041 1040 # parameters so such user is "corrupted", skipping the fill_data
1042 1041 for attr in ['user_id', 'username', 'admin', 'active']:
1043 1042 setattr(self, attr, None)
1044 1043 self.is_authenticated = False
1045 1044
1046 1045 if not self.username:
1047 1046 self.username = 'None'
1048 1047
1049 1048 log.debug('AuthUser: propagated user is now %s', self)
1050 1049
1051 1050 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1052 1051 calculate_super_admin=False, cache=False):
1053 1052 """
1054 1053 Fills user permission attribute with permissions taken from database
1055 1054 works for permissions given for repositories, and for permissions that
1056 1055 are granted to groups
1057 1056
1058 1057 :param user: instance of User object from database
1059 1058 :param explicit: In case there are permissions both for user and a group
1060 1059 that user is part of, explicit flag will defiine if user will
1061 1060 explicitly override permissions from group, if it's False it will
1062 1061 make decision based on the algo
1063 1062 :param algo: algorithm to decide what permission should be choose if
1064 1063 it's multiple defined, eg user in two different groups. It also
1065 1064 decides if explicit flag is turned off how to specify the permission
1066 1065 for case when user is in a group + have defined separate permission
1067 1066 """
1068 1067 user_id = user.user_id
1069 1068 user_is_admin = user.is_admin
1070 1069
1071 1070 # inheritance of global permissions like create repo/fork repo etc
1072 1071 user_inherit_default_permissions = user.inherit_default_permissions
1073 1072
1074 1073 log.debug('Computing PERMISSION tree for scope %s' % (scope, ))
1075 1074 compute = caches.conditional_cache(
1076 1075 'short_term', 'cache_desc',
1077 1076 condition=cache, func=_cached_perms_data)
1078 1077 result = compute(user_id, scope, user_is_admin,
1079 1078 user_inherit_default_permissions, explicit, algo,
1080 1079 calculate_super_admin)
1081 1080
1082 1081 result_repr = []
1083 1082 for k in result:
1084 1083 result_repr.append((k, len(result[k])))
1085 1084
1086 1085 log.debug('PERMISSION tree computed %s' % (result_repr,))
1087 1086 return result
1088 1087
1089 1088 @property
1090 1089 def is_default(self):
1091 1090 return self.username == User.DEFAULT_USER
1092 1091
1093 1092 @property
1094 1093 def is_admin(self):
1095 1094 return self.admin
1096 1095
1097 1096 @property
1098 1097 def is_user_object(self):
1099 1098 return self.user_id is not None
1100 1099
1101 1100 @property
1102 1101 def repositories_admin(self):
1103 1102 """
1104 1103 Returns list of repositories you're an admin of
1105 1104 """
1106 1105 return [
1107 1106 x[0] for x in self.permissions['repositories'].iteritems()
1108 1107 if x[1] == 'repository.admin']
1109 1108
1110 1109 @property
1111 1110 def repository_groups_admin(self):
1112 1111 """
1113 1112 Returns list of repository groups you're an admin of
1114 1113 """
1115 1114 return [
1116 1115 x[0] for x in self.permissions['repositories_groups'].iteritems()
1117 1116 if x[1] == 'group.admin']
1118 1117
1119 1118 @property
1120 1119 def user_groups_admin(self):
1121 1120 """
1122 1121 Returns list of user groups you're an admin of
1123 1122 """
1124 1123 return [
1125 1124 x[0] for x in self.permissions['user_groups'].iteritems()
1126 1125 if x[1] == 'usergroup.admin']
1127 1126
1128 1127 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1129 1128 """
1130 1129 Returns list of repository ids that user have access to based on given
1131 1130 perms. The cache flag should be only used in cases that are used for
1132 1131 display purposes, NOT IN ANY CASE for permission checks.
1133 1132 """
1134 1133 from rhodecode.model.scm import RepoList
1135 1134 if not perms:
1136 1135 perms = [
1137 1136 'repository.read', 'repository.write', 'repository.admin']
1138 1137
1139 1138 def _cached_repo_acl(user_id, perm_def, name_filter):
1140 1139 qry = Repository.query()
1141 1140 if name_filter:
1142 1141 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1143 1142 qry = qry.filter(
1144 1143 Repository.repo_name.ilike(ilike_expression))
1145 1144
1146 1145 return [x.repo_id for x in
1147 1146 RepoList(qry, perm_set=perm_def)]
1148 1147
1149 1148 compute = caches.conditional_cache(
1150 1149 'long_term', 'repo_acl_ids',
1151 1150 condition=cache, func=_cached_repo_acl)
1152 1151 return compute(self.user_id, perms, name_filter)
1153 1152
1154 1153 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1155 1154 """
1156 1155 Returns list of repository group ids that user have access to based on given
1157 1156 perms. The cache flag should be only used in cases that are used for
1158 1157 display purposes, NOT IN ANY CASE for permission checks.
1159 1158 """
1160 1159 from rhodecode.model.scm import RepoGroupList
1161 1160 if not perms:
1162 1161 perms = [
1163 1162 'group.read', 'group.write', 'group.admin']
1164 1163
1165 1164 def _cached_repo_group_acl(user_id, perm_def, name_filter):
1166 1165 qry = RepoGroup.query()
1167 1166 if name_filter:
1168 1167 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1169 1168 qry = qry.filter(
1170 1169 RepoGroup.group_name.ilike(ilike_expression))
1171 1170
1172 1171 return [x.group_id for x in
1173 1172 RepoGroupList(qry, perm_set=perm_def)]
1174 1173
1175 1174 compute = caches.conditional_cache(
1176 1175 'long_term', 'repo_group_acl_ids',
1177 1176 condition=cache, func=_cached_repo_group_acl)
1178 1177 return compute(self.user_id, perms, name_filter)
1179 1178
1180 1179 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1181 1180 """
1182 1181 Returns list of user group ids that user have access to based on given
1183 1182 perms. The cache flag should be only used in cases that are used for
1184 1183 display purposes, NOT IN ANY CASE for permission checks.
1185 1184 """
1186 1185 from rhodecode.model.scm import UserGroupList
1187 1186 if not perms:
1188 1187 perms = [
1189 1188 'usergroup.read', 'usergroup.write', 'usergroup.admin']
1190 1189
1191 1190 def _cached_user_group_acl(user_id, perm_def, name_filter):
1192 1191 qry = UserGroup.query()
1193 1192 if name_filter:
1194 1193 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1195 1194 qry = qry.filter(
1196 1195 UserGroup.users_group_name.ilike(ilike_expression))
1197 1196
1198 1197 return [x.users_group_id for x in
1199 1198 UserGroupList(qry, perm_set=perm_def)]
1200 1199
1201 1200 compute = caches.conditional_cache(
1202 1201 'long_term', 'user_group_acl_ids',
1203 1202 condition=cache, func=_cached_user_group_acl)
1204 1203 return compute(self.user_id, perms, name_filter)
1205 1204
1206 1205 @property
1207 1206 def ip_allowed(self):
1208 1207 """
1209 1208 Checks if ip_addr used in constructor is allowed from defined list of
1210 1209 allowed ip_addresses for user
1211 1210
1212 1211 :returns: boolean, True if ip is in allowed ip range
1213 1212 """
1214 1213 # check IP
1215 1214 inherit = self.inherit_default_permissions
1216 1215 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1217 1216 inherit_from_default=inherit)
1218 1217 @property
1219 1218 def personal_repo_group(self):
1220 1219 return RepoGroup.get_user_personal_repo_group(self.user_id)
1221 1220
1221 @LazyProperty
1222 def feed_token(self):
1223 return self.get_instance().feed_token
1224
1222 1225 @classmethod
1223 1226 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1224 1227 allowed_ips = AuthUser.get_allowed_ips(
1225 1228 user_id, cache=True, inherit_from_default=inherit_from_default)
1226 1229 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1227 1230 log.debug('IP:%s is in range of %s' % (ip_addr, allowed_ips))
1228 1231 return True
1229 1232 else:
1230 1233 log.info('Access for IP:%s forbidden, '
1231 1234 'not in %s' % (ip_addr, allowed_ips))
1232 1235 return False
1233 1236
1234 1237 def __repr__(self):
1235 1238 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1236 1239 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1237 1240
1238 1241 def set_authenticated(self, authenticated=True):
1239 1242 if self.user_id != self.anonymous_user.user_id:
1240 1243 self.is_authenticated = authenticated
1241 1244
1242 1245 def get_cookie_store(self):
1243 1246 return {
1244 1247 'username': self.username,
1245 1248 'password': md5(self.password or ''),
1246 1249 'user_id': self.user_id,
1247 1250 'is_authenticated': self.is_authenticated
1248 1251 }
1249 1252
1250 1253 @classmethod
1251 1254 def from_cookie_store(cls, cookie_store):
1252 1255 """
1253 1256 Creates AuthUser from a cookie store
1254 1257
1255 1258 :param cls:
1256 1259 :param cookie_store:
1257 1260 """
1258 1261 user_id = cookie_store.get('user_id')
1259 1262 username = cookie_store.get('username')
1260 1263 api_key = cookie_store.get('api_key')
1261 1264 return AuthUser(user_id, api_key, username)
1262 1265
1263 1266 @classmethod
1264 1267 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1265 1268 _set = set()
1266 1269
1267 1270 if inherit_from_default:
1268 1271 default_ips = UserIpMap.query().filter(
1269 1272 UserIpMap.user == User.get_default_user(cache=True))
1270 1273 if cache:
1271 1274 default_ips = default_ips.options(
1272 1275 FromCache("sql_cache_short", "get_user_ips_default"))
1273 1276
1274 1277 # populate from default user
1275 1278 for ip in default_ips:
1276 1279 try:
1277 1280 _set.add(ip.ip_addr)
1278 1281 except ObjectDeletedError:
1279 1282 # since we use heavy caching sometimes it happens that
1280 1283 # we get deleted objects here, we just skip them
1281 1284 pass
1282 1285
1283 1286 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1284 1287 if cache:
1285 1288 user_ips = user_ips.options(
1286 1289 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1287 1290
1288 1291 for ip in user_ips:
1289 1292 try:
1290 1293 _set.add(ip.ip_addr)
1291 1294 except ObjectDeletedError:
1292 1295 # since we use heavy caching sometimes it happens that we get
1293 1296 # deleted objects here, we just skip them
1294 1297 pass
1295 1298 return _set or set(['0.0.0.0/0', '::/0'])
1296 1299
1297 1300
1298 1301 def set_available_permissions(settings):
1299 1302 """
1300 1303 This function will propagate pyramid settings with all available defined
1301 1304 permission given in db. We don't want to check each time from db for new
1302 1305 permissions since adding a new permission also requires application restart
1303 1306 ie. to decorate new views with the newly created permission
1304 1307
1305 1308 :param settings: current pyramid registry.settings
1306 1309
1307 1310 """
1308 1311 log.debug('auth: getting information about all available permissions')
1309 1312 try:
1310 1313 sa = meta.Session
1311 1314 all_perms = sa.query(Permission).all()
1312 1315 settings.setdefault('available_permissions',
1313 1316 [x.permission_name for x in all_perms])
1314 1317 log.debug('auth: set available permissions')
1315 1318 except Exception:
1316 1319 log.exception('Failed to fetch permissions from the database.')
1317 1320 raise
1318 1321
1319 1322
1320 1323 def get_csrf_token(session, force_new=False, save_if_missing=True):
1321 1324 """
1322 1325 Return the current authentication token, creating one if one doesn't
1323 1326 already exist and the save_if_missing flag is present.
1324 1327
1325 1328 :param session: pass in the pyramid session, else we use the global ones
1326 1329 :param force_new: force to re-generate the token and store it in session
1327 1330 :param save_if_missing: save the newly generated token if it's missing in
1328 1331 session
1329 1332 """
1330 1333 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1331 1334 # from pyramid.csrf import get_csrf_token
1332 1335
1333 1336 if (csrf_token_key not in session and save_if_missing) or force_new:
1334 1337 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1335 1338 session[csrf_token_key] = token
1336 1339 if hasattr(session, 'save'):
1337 1340 session.save()
1338 1341 return session.get(csrf_token_key)
1339 1342
1340 1343
1341 1344 def get_request(perm_class_instance):
1342 1345 from pyramid.threadlocal import get_current_request
1343 1346 pyramid_request = get_current_request()
1344 1347 return pyramid_request
1345 1348
1346 1349
1347 1350 # CHECK DECORATORS
1348 1351 class CSRFRequired(object):
1349 1352 """
1350 1353 Decorator for authenticating a form
1351 1354
1352 1355 This decorator uses an authorization token stored in the client's
1353 1356 session for prevention of certain Cross-site request forgery (CSRF)
1354 1357 attacks (See
1355 1358 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1356 1359 information).
1357 1360
1358 1361 For use with the ``webhelpers.secure_form`` helper functions.
1359 1362
1360 1363 """
1361 1364 def __init__(self, token=csrf_token_key, header='X-CSRF-Token',
1362 1365 except_methods=None):
1363 1366 self.token = token
1364 1367 self.header = header
1365 1368 self.except_methods = except_methods or []
1366 1369
1367 1370 def __call__(self, func):
1368 1371 return get_cython_compat_decorator(self.__wrapper, func)
1369 1372
1370 1373 def _get_csrf(self, _request):
1371 1374 return _request.POST.get(self.token, _request.headers.get(self.header))
1372 1375
1373 1376 def check_csrf(self, _request, cur_token):
1374 1377 supplied_token = self._get_csrf(_request)
1375 1378 return supplied_token and supplied_token == cur_token
1376 1379
1377 1380 def _get_request(self):
1378 1381 return get_request(self)
1379 1382
1380 1383 def __wrapper(self, func, *fargs, **fkwargs):
1381 1384 request = self._get_request()
1382 1385
1383 1386 if request.method in self.except_methods:
1384 1387 return func(*fargs, **fkwargs)
1385 1388
1386 1389 cur_token = get_csrf_token(request.session, save_if_missing=False)
1387 1390 if self.check_csrf(request, cur_token):
1388 1391 if request.POST.get(self.token):
1389 1392 del request.POST[self.token]
1390 1393 return func(*fargs, **fkwargs)
1391 1394 else:
1392 1395 reason = 'token-missing'
1393 1396 supplied_token = self._get_csrf(request)
1394 1397 if supplied_token and cur_token != supplied_token:
1395 1398 reason = 'token-mismatch [%s:%s]' % (
1396 1399 cur_token or ''[:6], supplied_token or ''[:6])
1397 1400
1398 1401 csrf_message = \
1399 1402 ("Cross-site request forgery detected, request denied. See "
1400 1403 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1401 1404 "more information.")
1402 1405 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1403 1406 'REMOTE_ADDR:%s, HEADERS:%s' % (
1404 1407 request, reason, request.remote_addr, request.headers))
1405 1408
1406 1409 raise HTTPForbidden(explanation=csrf_message)
1407 1410
1408 1411
1409 1412 class LoginRequired(object):
1410 1413 """
1411 1414 Must be logged in to execute this function else
1412 1415 redirect to login page
1413 1416
1414 1417 :param api_access: if enabled this checks only for valid auth token
1415 1418 and grants access based on valid token
1416 1419 """
1417 1420 def __init__(self, auth_token_access=None):
1418 1421 self.auth_token_access = auth_token_access
1419 1422
1420 1423 def __call__(self, func):
1421 1424 return get_cython_compat_decorator(self.__wrapper, func)
1422 1425
1423 1426 def _get_request(self):
1424 1427 return get_request(self)
1425 1428
1426 1429 def __wrapper(self, func, *fargs, **fkwargs):
1427 1430 from rhodecode.lib import helpers as h
1428 1431 cls = fargs[0]
1429 1432 user = cls._rhodecode_user
1430 1433 request = self._get_request()
1431 1434 _ = request.translate
1432 1435
1433 1436 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1434 1437 log.debug('Starting login restriction checks for user: %s' % (user,))
1435 1438 # check if our IP is allowed
1436 1439 ip_access_valid = True
1437 1440 if not user.ip_allowed:
1438 1441 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr,))),
1439 1442 category='warning')
1440 1443 ip_access_valid = False
1441 1444
1442 1445 # check if we used an APIKEY and it's a valid one
1443 1446 # defined white-list of controllers which API access will be enabled
1444 1447 _auth_token = request.GET.get(
1445 1448 'auth_token', '') or request.GET.get('api_key', '')
1446 1449 auth_token_access_valid = allowed_auth_token_access(
1447 1450 loc, auth_token=_auth_token)
1448 1451
1449 1452 # explicit controller is enabled or API is in our whitelist
1450 1453 if self.auth_token_access or auth_token_access_valid:
1451 1454 log.debug('Checking AUTH TOKEN access for %s' % (cls,))
1452 1455 db_user = user.get_instance()
1453 1456
1454 1457 if db_user:
1455 1458 if self.auth_token_access:
1456 1459 roles = self.auth_token_access
1457 1460 else:
1458 1461 roles = [UserApiKeys.ROLE_HTTP]
1459 1462 token_match = db_user.authenticate_by_token(
1460 1463 _auth_token, roles=roles)
1461 1464 else:
1462 1465 log.debug('Unable to fetch db instance for auth user: %s', user)
1463 1466 token_match = False
1464 1467
1465 1468 if _auth_token and token_match:
1466 1469 auth_token_access_valid = True
1467 1470 log.debug('AUTH TOKEN ****%s is VALID' % (_auth_token[-4:],))
1468 1471 else:
1469 1472 auth_token_access_valid = False
1470 1473 if not _auth_token:
1471 1474 log.debug("AUTH TOKEN *NOT* present in request")
1472 1475 else:
1473 1476 log.warning(
1474 1477 "AUTH TOKEN ****%s *NOT* valid" % _auth_token[-4:])
1475 1478
1476 1479 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
1477 1480 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1478 1481 else 'AUTH_TOKEN_AUTH'
1479 1482
1480 1483 if ip_access_valid and (
1481 1484 user.is_authenticated or auth_token_access_valid):
1482 1485 log.info(
1483 1486 'user %s authenticating with:%s IS authenticated on func %s'
1484 1487 % (user, reason, loc))
1485 1488
1486 1489 # update user data to check last activity
1487 1490 user.update_lastactivity()
1488 1491 Session().commit()
1489 1492 return func(*fargs, **fkwargs)
1490 1493 else:
1491 1494 log.warning(
1492 1495 'user %s authenticating with:%s NOT authenticated on '
1493 1496 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s'
1494 1497 % (user, reason, loc, ip_access_valid,
1495 1498 auth_token_access_valid))
1496 1499 # we preserve the get PARAM
1497 1500 came_from = get_came_from(request)
1498 1501
1499 1502 log.debug('redirecting to login page with %s' % (came_from,))
1500 1503 raise HTTPFound(
1501 1504 h.route_path('login', _query={'came_from': came_from}))
1502 1505
1503 1506
1504 1507 class NotAnonymous(object):
1505 1508 """
1506 1509 Must be logged in to execute this function else
1507 1510 redirect to login page
1508 1511 """
1509 1512
1510 1513 def __call__(self, func):
1511 1514 return get_cython_compat_decorator(self.__wrapper, func)
1512 1515
1513 1516 def _get_request(self):
1514 1517 return get_request(self)
1515 1518
1516 1519 def __wrapper(self, func, *fargs, **fkwargs):
1517 1520 import rhodecode.lib.helpers as h
1518 1521 cls = fargs[0]
1519 1522 self.user = cls._rhodecode_user
1520 1523 request = self._get_request()
1521 1524 _ = request.translate
1522 1525 log.debug('Checking if user is not anonymous @%s' % cls)
1523 1526
1524 1527 anonymous = self.user.username == User.DEFAULT_USER
1525 1528
1526 1529 if anonymous:
1527 1530 came_from = get_came_from(request)
1528 1531 h.flash(_('You need to be a registered user to '
1529 1532 'perform this action'),
1530 1533 category='warning')
1531 1534 raise HTTPFound(
1532 1535 h.route_path('login', _query={'came_from': came_from}))
1533 1536 else:
1534 1537 return func(*fargs, **fkwargs)
1535 1538
1536 1539
1537 1540 class PermsDecorator(object):
1538 1541 """
1539 1542 Base class for controller decorators, we extract the current user from
1540 1543 the class itself, which has it stored in base controllers
1541 1544 """
1542 1545
1543 1546 def __init__(self, *required_perms):
1544 1547 self.required_perms = set(required_perms)
1545 1548
1546 1549 def __call__(self, func):
1547 1550 return get_cython_compat_decorator(self.__wrapper, func)
1548 1551
1549 1552 def _get_request(self):
1550 1553 return get_request(self)
1551 1554
1552 1555 def __wrapper(self, func, *fargs, **fkwargs):
1553 1556 import rhodecode.lib.helpers as h
1554 1557 cls = fargs[0]
1555 1558 _user = cls._rhodecode_user
1556 1559 request = self._get_request()
1557 1560 _ = request.translate
1558 1561
1559 1562 log.debug('checking %s permissions %s for %s %s',
1560 1563 self.__class__.__name__, self.required_perms, cls, _user)
1561 1564
1562 1565 if self.check_permissions(_user):
1563 1566 log.debug('Permission granted for %s %s', cls, _user)
1564 1567 return func(*fargs, **fkwargs)
1565 1568
1566 1569 else:
1567 1570 log.debug('Permission denied for %s %s', cls, _user)
1568 1571 anonymous = _user.username == User.DEFAULT_USER
1569 1572
1570 1573 if anonymous:
1571 1574 came_from = get_came_from(self._get_request())
1572 1575 h.flash(_('You need to be signed in to view this page'),
1573 1576 category='warning')
1574 1577 raise HTTPFound(
1575 1578 h.route_path('login', _query={'came_from': came_from}))
1576 1579
1577 1580 else:
1578 1581 # redirect with 404 to prevent resource discovery
1579 1582 raise HTTPNotFound()
1580 1583
1581 1584 def check_permissions(self, user):
1582 1585 """Dummy function for overriding"""
1583 1586 raise NotImplementedError(
1584 1587 'You have to write this function in child class')
1585 1588
1586 1589
1587 1590 class HasPermissionAllDecorator(PermsDecorator):
1588 1591 """
1589 1592 Checks for access permission for all given predicates. All of them
1590 1593 have to be meet in order to fulfill the request
1591 1594 """
1592 1595
1593 1596 def check_permissions(self, user):
1594 1597 perms = user.permissions_with_scope({})
1595 1598 if self.required_perms.issubset(perms['global']):
1596 1599 return True
1597 1600 return False
1598 1601
1599 1602
1600 1603 class HasPermissionAnyDecorator(PermsDecorator):
1601 1604 """
1602 1605 Checks for access permission for any of given predicates. In order to
1603 1606 fulfill the request any of predicates must be meet
1604 1607 """
1605 1608
1606 1609 def check_permissions(self, user):
1607 1610 perms = user.permissions_with_scope({})
1608 1611 if self.required_perms.intersection(perms['global']):
1609 1612 return True
1610 1613 return False
1611 1614
1612 1615
1613 1616 class HasRepoPermissionAllDecorator(PermsDecorator):
1614 1617 """
1615 1618 Checks for access permission for all given predicates for specific
1616 1619 repository. All of them have to be meet in order to fulfill the request
1617 1620 """
1618 1621 def _get_repo_name(self):
1619 1622 _request = self._get_request()
1620 1623 return get_repo_slug(_request)
1621 1624
1622 1625 def check_permissions(self, user):
1623 1626 perms = user.permissions
1624 1627 repo_name = self._get_repo_name()
1625 1628
1626 1629 try:
1627 1630 user_perms = set([perms['repositories'][repo_name]])
1628 1631 except KeyError:
1629 1632 log.debug('cannot locate repo with name: `%s` in permissions defs',
1630 1633 repo_name)
1631 1634 return False
1632 1635
1633 1636 log.debug('checking `%s` permissions for repo `%s`',
1634 1637 user_perms, repo_name)
1635 1638 if self.required_perms.issubset(user_perms):
1636 1639 return True
1637 1640 return False
1638 1641
1639 1642
1640 1643 class HasRepoPermissionAnyDecorator(PermsDecorator):
1641 1644 """
1642 1645 Checks for access permission for any of given predicates for specific
1643 1646 repository. In order to fulfill the request any of predicates must be meet
1644 1647 """
1645 1648 def _get_repo_name(self):
1646 1649 _request = self._get_request()
1647 1650 return get_repo_slug(_request)
1648 1651
1649 1652 def check_permissions(self, user):
1650 1653 perms = user.permissions
1651 1654 repo_name = self._get_repo_name()
1652 1655
1653 1656 try:
1654 1657 user_perms = set([perms['repositories'][repo_name]])
1655 1658 except KeyError:
1656 1659 log.debug(
1657 1660 'cannot locate repo with name: `%s` in permissions defs',
1658 1661 repo_name)
1659 1662 return False
1660 1663
1661 1664 log.debug('checking `%s` permissions for repo `%s`',
1662 1665 user_perms, repo_name)
1663 1666 if self.required_perms.intersection(user_perms):
1664 1667 return True
1665 1668 return False
1666 1669
1667 1670
1668 1671 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
1669 1672 """
1670 1673 Checks for access permission for all given predicates for specific
1671 1674 repository group. All of them have to be meet in order to
1672 1675 fulfill the request
1673 1676 """
1674 1677 def _get_repo_group_name(self):
1675 1678 _request = self._get_request()
1676 1679 return get_repo_group_slug(_request)
1677 1680
1678 1681 def check_permissions(self, user):
1679 1682 perms = user.permissions
1680 1683 group_name = self._get_repo_group_name()
1681 1684 try:
1682 1685 user_perms = set([perms['repositories_groups'][group_name]])
1683 1686 except KeyError:
1684 1687 log.debug(
1685 1688 'cannot locate repo group with name: `%s` in permissions defs',
1686 1689 group_name)
1687 1690 return False
1688 1691
1689 1692 log.debug('checking `%s` permissions for repo group `%s`',
1690 1693 user_perms, group_name)
1691 1694 if self.required_perms.issubset(user_perms):
1692 1695 return True
1693 1696 return False
1694 1697
1695 1698
1696 1699 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
1697 1700 """
1698 1701 Checks for access permission for any of given predicates for specific
1699 1702 repository group. In order to fulfill the request any
1700 1703 of predicates must be met
1701 1704 """
1702 1705 def _get_repo_group_name(self):
1703 1706 _request = self._get_request()
1704 1707 return get_repo_group_slug(_request)
1705 1708
1706 1709 def check_permissions(self, user):
1707 1710 perms = user.permissions
1708 1711 group_name = self._get_repo_group_name()
1709 1712
1710 1713 try:
1711 1714 user_perms = set([perms['repositories_groups'][group_name]])
1712 1715 except KeyError:
1713 1716 log.debug(
1714 1717 'cannot locate repo group with name: `%s` in permissions defs',
1715 1718 group_name)
1716 1719 return False
1717 1720
1718 1721 log.debug('checking `%s` permissions for repo group `%s`',
1719 1722 user_perms, group_name)
1720 1723 if self.required_perms.intersection(user_perms):
1721 1724 return True
1722 1725 return False
1723 1726
1724 1727
1725 1728 class HasUserGroupPermissionAllDecorator(PermsDecorator):
1726 1729 """
1727 1730 Checks for access permission for all given predicates for specific
1728 1731 user group. All of them have to be meet in order to fulfill the request
1729 1732 """
1730 1733 def _get_user_group_name(self):
1731 1734 _request = self._get_request()
1732 1735 return get_user_group_slug(_request)
1733 1736
1734 1737 def check_permissions(self, user):
1735 1738 perms = user.permissions
1736 1739 group_name = self._get_user_group_name()
1737 1740 try:
1738 1741 user_perms = set([perms['user_groups'][group_name]])
1739 1742 except KeyError:
1740 1743 return False
1741 1744
1742 1745 if self.required_perms.issubset(user_perms):
1743 1746 return True
1744 1747 return False
1745 1748
1746 1749
1747 1750 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
1748 1751 """
1749 1752 Checks for access permission for any of given predicates for specific
1750 1753 user group. In order to fulfill the request any of predicates must be meet
1751 1754 """
1752 1755 def _get_user_group_name(self):
1753 1756 _request = self._get_request()
1754 1757 return get_user_group_slug(_request)
1755 1758
1756 1759 def check_permissions(self, user):
1757 1760 perms = user.permissions
1758 1761 group_name = self._get_user_group_name()
1759 1762 try:
1760 1763 user_perms = set([perms['user_groups'][group_name]])
1761 1764 except KeyError:
1762 1765 return False
1763 1766
1764 1767 if self.required_perms.intersection(user_perms):
1765 1768 return True
1766 1769 return False
1767 1770
1768 1771
1769 1772 # CHECK FUNCTIONS
1770 1773 class PermsFunction(object):
1771 1774 """Base function for other check functions"""
1772 1775
1773 1776 def __init__(self, *perms):
1774 1777 self.required_perms = set(perms)
1775 1778 self.repo_name = None
1776 1779 self.repo_group_name = None
1777 1780 self.user_group_name = None
1778 1781
1779 1782 def __bool__(self):
1780 1783 frame = inspect.currentframe()
1781 1784 stack_trace = traceback.format_stack(frame)
1782 1785 log.error('Checking bool value on a class instance of perm '
1783 1786 'function is not allowed: %s' % ''.join(stack_trace))
1784 1787 # rather than throwing errors, here we always return False so if by
1785 1788 # accident someone checks truth for just an instance it will always end
1786 1789 # up in returning False
1787 1790 return False
1788 1791 __nonzero__ = __bool__
1789 1792
1790 1793 def __call__(self, check_location='', user=None):
1791 1794 if not user:
1792 1795 log.debug('Using user attribute from global request')
1793 1796 # TODO: remove this someday,put as user as attribute here
1794 1797 request = self._get_request()
1795 1798 user = request.user
1796 1799
1797 1800 # init auth user if not already given
1798 1801 if not isinstance(user, AuthUser):
1799 1802 log.debug('Wrapping user %s into AuthUser', user)
1800 1803 user = AuthUser(user.user_id)
1801 1804
1802 1805 cls_name = self.__class__.__name__
1803 1806 check_scope = self._get_check_scope(cls_name)
1804 1807 check_location = check_location or 'unspecified location'
1805 1808
1806 1809 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
1807 1810 self.required_perms, user, check_scope, check_location)
1808 1811 if not user:
1809 1812 log.warning('Empty user given for permission check')
1810 1813 return False
1811 1814
1812 1815 if self.check_permissions(user):
1813 1816 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
1814 1817 check_scope, user, check_location)
1815 1818 return True
1816 1819
1817 1820 else:
1818 1821 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
1819 1822 check_scope, user, check_location)
1820 1823 return False
1821 1824
1822 1825 def _get_request(self):
1823 1826 return get_request(self)
1824 1827
1825 1828 def _get_check_scope(self, cls_name):
1826 1829 return {
1827 1830 'HasPermissionAll': 'GLOBAL',
1828 1831 'HasPermissionAny': 'GLOBAL',
1829 1832 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
1830 1833 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
1831 1834 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
1832 1835 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
1833 1836 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
1834 1837 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
1835 1838 }.get(cls_name, '?:%s' % cls_name)
1836 1839
1837 1840 def check_permissions(self, user):
1838 1841 """Dummy function for overriding"""
1839 1842 raise Exception('You have to write this function in child class')
1840 1843
1841 1844
1842 1845 class HasPermissionAll(PermsFunction):
1843 1846 def check_permissions(self, user):
1844 1847 perms = user.permissions_with_scope({})
1845 1848 if self.required_perms.issubset(perms.get('global')):
1846 1849 return True
1847 1850 return False
1848 1851
1849 1852
1850 1853 class HasPermissionAny(PermsFunction):
1851 1854 def check_permissions(self, user):
1852 1855 perms = user.permissions_with_scope({})
1853 1856 if self.required_perms.intersection(perms.get('global')):
1854 1857 return True
1855 1858 return False
1856 1859
1857 1860
1858 1861 class HasRepoPermissionAll(PermsFunction):
1859 1862 def __call__(self, repo_name=None, check_location='', user=None):
1860 1863 self.repo_name = repo_name
1861 1864 return super(HasRepoPermissionAll, self).__call__(check_location, user)
1862 1865
1863 1866 def _get_repo_name(self):
1864 1867 if not self.repo_name:
1865 1868 _request = self._get_request()
1866 1869 self.repo_name = get_repo_slug(_request)
1867 1870 return self.repo_name
1868 1871
1869 1872 def check_permissions(self, user):
1870 1873 self.repo_name = self._get_repo_name()
1871 1874 perms = user.permissions
1872 1875 try:
1873 1876 user_perms = set([perms['repositories'][self.repo_name]])
1874 1877 except KeyError:
1875 1878 return False
1876 1879 if self.required_perms.issubset(user_perms):
1877 1880 return True
1878 1881 return False
1879 1882
1880 1883
1881 1884 class HasRepoPermissionAny(PermsFunction):
1882 1885 def __call__(self, repo_name=None, check_location='', user=None):
1883 1886 self.repo_name = repo_name
1884 1887 return super(HasRepoPermissionAny, self).__call__(check_location, user)
1885 1888
1886 1889 def _get_repo_name(self):
1887 1890 if not self.repo_name:
1888 1891 _request = self._get_request()
1889 1892 self.repo_name = get_repo_slug(_request)
1890 1893 return self.repo_name
1891 1894
1892 1895 def check_permissions(self, user):
1893 1896 self.repo_name = self._get_repo_name()
1894 1897 perms = user.permissions
1895 1898 try:
1896 1899 user_perms = set([perms['repositories'][self.repo_name]])
1897 1900 except KeyError:
1898 1901 return False
1899 1902 if self.required_perms.intersection(user_perms):
1900 1903 return True
1901 1904 return False
1902 1905
1903 1906
1904 1907 class HasRepoGroupPermissionAny(PermsFunction):
1905 1908 def __call__(self, group_name=None, check_location='', user=None):
1906 1909 self.repo_group_name = group_name
1907 1910 return super(HasRepoGroupPermissionAny, self).__call__(
1908 1911 check_location, user)
1909 1912
1910 1913 def check_permissions(self, user):
1911 1914 perms = user.permissions
1912 1915 try:
1913 1916 user_perms = set(
1914 1917 [perms['repositories_groups'][self.repo_group_name]])
1915 1918 except KeyError:
1916 1919 return False
1917 1920 if self.required_perms.intersection(user_perms):
1918 1921 return True
1919 1922 return False
1920 1923
1921 1924
1922 1925 class HasRepoGroupPermissionAll(PermsFunction):
1923 1926 def __call__(self, group_name=None, check_location='', user=None):
1924 1927 self.repo_group_name = group_name
1925 1928 return super(HasRepoGroupPermissionAll, self).__call__(
1926 1929 check_location, user)
1927 1930
1928 1931 def check_permissions(self, user):
1929 1932 perms = user.permissions
1930 1933 try:
1931 1934 user_perms = set(
1932 1935 [perms['repositories_groups'][self.repo_group_name]])
1933 1936 except KeyError:
1934 1937 return False
1935 1938 if self.required_perms.issubset(user_perms):
1936 1939 return True
1937 1940 return False
1938 1941
1939 1942
1940 1943 class HasUserGroupPermissionAny(PermsFunction):
1941 1944 def __call__(self, user_group_name=None, check_location='', user=None):
1942 1945 self.user_group_name = user_group_name
1943 1946 return super(HasUserGroupPermissionAny, self).__call__(
1944 1947 check_location, user)
1945 1948
1946 1949 def check_permissions(self, user):
1947 1950 perms = user.permissions
1948 1951 try:
1949 1952 user_perms = set([perms['user_groups'][self.user_group_name]])
1950 1953 except KeyError:
1951 1954 return False
1952 1955 if self.required_perms.intersection(user_perms):
1953 1956 return True
1954 1957 return False
1955 1958
1956 1959
1957 1960 class HasUserGroupPermissionAll(PermsFunction):
1958 1961 def __call__(self, user_group_name=None, check_location='', user=None):
1959 1962 self.user_group_name = user_group_name
1960 1963 return super(HasUserGroupPermissionAll, self).__call__(
1961 1964 check_location, user)
1962 1965
1963 1966 def check_permissions(self, user):
1964 1967 perms = user.permissions
1965 1968 try:
1966 1969 user_perms = set([perms['user_groups'][self.user_group_name]])
1967 1970 except KeyError:
1968 1971 return False
1969 1972 if self.required_perms.issubset(user_perms):
1970 1973 return True
1971 1974 return False
1972 1975
1973 1976
1974 1977 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
1975 1978 class HasPermissionAnyMiddleware(object):
1976 1979 def __init__(self, *perms):
1977 1980 self.required_perms = set(perms)
1978 1981
1979 1982 def __call__(self, user, repo_name):
1980 1983 # repo_name MUST be unicode, since we handle keys in permission
1981 1984 # dict by unicode
1982 1985 repo_name = safe_unicode(repo_name)
1983 1986 user = AuthUser(user.user_id)
1984 1987 log.debug(
1985 1988 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
1986 1989 self.required_perms, user, repo_name)
1987 1990
1988 1991 if self.check_permissions(user, repo_name):
1989 1992 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
1990 1993 repo_name, user, 'PermissionMiddleware')
1991 1994 return True
1992 1995
1993 1996 else:
1994 1997 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
1995 1998 repo_name, user, 'PermissionMiddleware')
1996 1999 return False
1997 2000
1998 2001 def check_permissions(self, user, repo_name):
1999 2002 perms = user.permissions_with_scope({'repo_name': repo_name})
2000 2003
2001 2004 try:
2002 2005 user_perms = set([perms['repositories'][repo_name]])
2003 2006 except Exception:
2004 2007 log.exception('Error while accessing user permissions')
2005 2008 return False
2006 2009
2007 2010 if self.required_perms.intersection(user_perms):
2008 2011 return True
2009 2012 return False
2010 2013
2011 2014
2012 2015 # SPECIAL VERSION TO HANDLE API AUTH
2013 2016 class _BaseApiPerm(object):
2014 2017 def __init__(self, *perms):
2015 2018 self.required_perms = set(perms)
2016 2019
2017 2020 def __call__(self, check_location=None, user=None, repo_name=None,
2018 2021 group_name=None, user_group_name=None):
2019 2022 cls_name = self.__class__.__name__
2020 2023 check_scope = 'global:%s' % (self.required_perms,)
2021 2024 if repo_name:
2022 2025 check_scope += ', repo_name:%s' % (repo_name,)
2023 2026
2024 2027 if group_name:
2025 2028 check_scope += ', repo_group_name:%s' % (group_name,)
2026 2029
2027 2030 if user_group_name:
2028 2031 check_scope += ', user_group_name:%s' % (user_group_name,)
2029 2032
2030 2033 log.debug(
2031 2034 'checking cls:%s %s %s @ %s'
2032 2035 % (cls_name, self.required_perms, check_scope, check_location))
2033 2036 if not user:
2034 2037 log.debug('Empty User passed into arguments')
2035 2038 return False
2036 2039
2037 2040 # process user
2038 2041 if not isinstance(user, AuthUser):
2039 2042 user = AuthUser(user.user_id)
2040 2043 if not check_location:
2041 2044 check_location = 'unspecified'
2042 2045 if self.check_permissions(user.permissions, repo_name, group_name,
2043 2046 user_group_name):
2044 2047 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2045 2048 check_scope, user, check_location)
2046 2049 return True
2047 2050
2048 2051 else:
2049 2052 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2050 2053 check_scope, user, check_location)
2051 2054 return False
2052 2055
2053 2056 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2054 2057 user_group_name=None):
2055 2058 """
2056 2059 implement in child class should return True if permissions are ok,
2057 2060 False otherwise
2058 2061
2059 2062 :param perm_defs: dict with permission definitions
2060 2063 :param repo_name: repo name
2061 2064 """
2062 2065 raise NotImplementedError()
2063 2066
2064 2067
2065 2068 class HasPermissionAllApi(_BaseApiPerm):
2066 2069 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2067 2070 user_group_name=None):
2068 2071 if self.required_perms.issubset(perm_defs.get('global')):
2069 2072 return True
2070 2073 return False
2071 2074
2072 2075
2073 2076 class HasPermissionAnyApi(_BaseApiPerm):
2074 2077 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2075 2078 user_group_name=None):
2076 2079 if self.required_perms.intersection(perm_defs.get('global')):
2077 2080 return True
2078 2081 return False
2079 2082
2080 2083
2081 2084 class HasRepoPermissionAllApi(_BaseApiPerm):
2082 2085 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2083 2086 user_group_name=None):
2084 2087 try:
2085 2088 _user_perms = set([perm_defs['repositories'][repo_name]])
2086 2089 except KeyError:
2087 2090 log.warning(traceback.format_exc())
2088 2091 return False
2089 2092 if self.required_perms.issubset(_user_perms):
2090 2093 return True
2091 2094 return False
2092 2095
2093 2096
2094 2097 class HasRepoPermissionAnyApi(_BaseApiPerm):
2095 2098 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2096 2099 user_group_name=None):
2097 2100 try:
2098 2101 _user_perms = set([perm_defs['repositories'][repo_name]])
2099 2102 except KeyError:
2100 2103 log.warning(traceback.format_exc())
2101 2104 return False
2102 2105 if self.required_perms.intersection(_user_perms):
2103 2106 return True
2104 2107 return False
2105 2108
2106 2109
2107 2110 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2108 2111 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2109 2112 user_group_name=None):
2110 2113 try:
2111 2114 _user_perms = set([perm_defs['repositories_groups'][group_name]])
2112 2115 except KeyError:
2113 2116 log.warning(traceback.format_exc())
2114 2117 return False
2115 2118 if self.required_perms.intersection(_user_perms):
2116 2119 return True
2117 2120 return False
2118 2121
2119 2122
2120 2123 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2121 2124 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2122 2125 user_group_name=None):
2123 2126 try:
2124 2127 _user_perms = set([perm_defs['repositories_groups'][group_name]])
2125 2128 except KeyError:
2126 2129 log.warning(traceback.format_exc())
2127 2130 return False
2128 2131 if self.required_perms.issubset(_user_perms):
2129 2132 return True
2130 2133 return False
2131 2134
2132 2135
2133 2136 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2134 2137 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2135 2138 user_group_name=None):
2136 2139 try:
2137 2140 _user_perms = set([perm_defs['user_groups'][user_group_name]])
2138 2141 except KeyError:
2139 2142 log.warning(traceback.format_exc())
2140 2143 return False
2141 2144 if self.required_perms.intersection(_user_perms):
2142 2145 return True
2143 2146 return False
2144 2147
2145 2148
2146 2149 def check_ip_access(source_ip, allowed_ips=None):
2147 2150 """
2148 2151 Checks if source_ip is a subnet of any of allowed_ips.
2149 2152
2150 2153 :param source_ip:
2151 2154 :param allowed_ips: list of allowed ips together with mask
2152 2155 """
2153 2156 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
2154 2157 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2155 2158 if isinstance(allowed_ips, (tuple, list, set)):
2156 2159 for ip in allowed_ips:
2157 2160 ip = safe_unicode(ip)
2158 2161 try:
2159 2162 network_address = ipaddress.ip_network(ip, strict=False)
2160 2163 if source_ip_address in network_address:
2161 2164 log.debug('IP %s is network %s' %
2162 2165 (source_ip_address, network_address))
2163 2166 return True
2164 2167 # for any case we cannot determine the IP, don't crash just
2165 2168 # skip it and log as error, we want to say forbidden still when
2166 2169 # sending bad IP
2167 2170 except Exception:
2168 2171 log.error(traceback.format_exc())
2169 2172 continue
2170 2173 return False
2171 2174
2172 2175
2173 2176 def get_cython_compat_decorator(wrapper, func):
2174 2177 """
2175 2178 Creates a cython compatible decorator. The previously used
2176 2179 decorator.decorator() function seems to be incompatible with cython.
2177 2180
2178 2181 :param wrapper: __wrapper method of the decorator class
2179 2182 :param func: decorated function
2180 2183 """
2181 2184 @wraps(func)
2182 2185 def local_wrapper(*args, **kwds):
2183 2186 return wrapper(func, *args, **kwds)
2184 2187 local_wrapper.__wrapped__ = func
2185 2188 return local_wrapper
2186 2189
2187 2190
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,911 +1,909 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 users model for RhodeCode
23 23 """
24 24
25 25 import logging
26 26 import traceback
27 27 import datetime
28 28 import ipaddress
29 29
30 30 from pyramid.threadlocal import get_current_request
31 31 from sqlalchemy.exc import DatabaseError
32 32
33 33 from rhodecode import events
34 34 from rhodecode.lib.user_log_filter import user_log_filter
35 35 from rhodecode.lib.utils2 import (
36 36 safe_unicode, get_current_rhodecode_user, action_logger_generic,
37 37 AttributeDict, str2bool)
38 38 from rhodecode.lib.exceptions import (
39 39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
40 40 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
41 41 from rhodecode.lib.caching_query import FromCache
42 42 from rhodecode.model import BaseModel
43 43 from rhodecode.model.auth_token import AuthTokenModel
44 44 from rhodecode.model.db import (
45 45 _hash_key, true, false, or_, joinedload, User, UserToPerm,
46 46 UserEmailMap, UserIpMap, UserLog)
47 47 from rhodecode.model.meta import Session
48 48 from rhodecode.model.repo_group import RepoGroupModel
49 49
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 class UserModel(BaseModel):
55 55 cls = User
56 56
57 57 def get(self, user_id, cache=False):
58 58 user = self.sa.query(User)
59 59 if cache:
60 60 user = user.options(
61 61 FromCache("sql_cache_short", "get_user_%s" % user_id))
62 62 return user.get(user_id)
63 63
64 64 def get_user(self, user):
65 65 return self._get_user(user)
66 66
67 67 def _serialize_user(self, user):
68 68 import rhodecode.lib.helpers as h
69 69
70 70 return {
71 71 'id': user.user_id,
72 72 'first_name': user.first_name,
73 73 'last_name': user.last_name,
74 74 'username': user.username,
75 75 'email': user.email,
76 76 'icon_link': h.gravatar_url(user.email, 30),
77 77 'value_display': h.escape(h.person(user)),
78 78 'value': user.username,
79 79 'value_type': 'user',
80 80 'active': user.active,
81 81 }
82 82
83 83 def get_users(self, name_contains=None, limit=20, only_active=True):
84 84
85 85 query = self.sa.query(User)
86 86 if only_active:
87 87 query = query.filter(User.active == true())
88 88
89 89 if name_contains:
90 90 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
91 91 query = query.filter(
92 92 or_(
93 93 User.name.ilike(ilike_expression),
94 94 User.lastname.ilike(ilike_expression),
95 95 User.username.ilike(ilike_expression)
96 96 )
97 97 )
98 98 query = query.limit(limit)
99 99 users = query.all()
100 100
101 101 _users = [
102 102 self._serialize_user(user) for user in users
103 103 ]
104 104 return _users
105 105
106 106 def get_by_username(self, username, cache=False, case_insensitive=False):
107 107
108 108 if case_insensitive:
109 109 user = self.sa.query(User).filter(User.username.ilike(username))
110 110 else:
111 111 user = self.sa.query(User)\
112 112 .filter(User.username == username)
113 113 if cache:
114 114 name_key = _hash_key(username)
115 115 user = user.options(
116 116 FromCache("sql_cache_short", "get_user_%s" % name_key))
117 117 return user.scalar()
118 118
119 119 def get_by_email(self, email, cache=False, case_insensitive=False):
120 120 return User.get_by_email(email, case_insensitive, cache)
121 121
122 122 def get_by_auth_token(self, auth_token, cache=False):
123 123 return User.get_by_auth_token(auth_token, cache)
124 124
125 125 def get_active_user_count(self, cache=False):
126 126 return User.query().filter(
127 127 User.active == True).filter(
128 128 User.username != User.DEFAULT_USER).count()
129 129
130 130 def create(self, form_data, cur_user=None):
131 131 if not cur_user:
132 132 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
133 133
134 134 user_data = {
135 135 'username': form_data['username'],
136 136 'password': form_data['password'],
137 137 'email': form_data['email'],
138 138 'firstname': form_data['firstname'],
139 139 'lastname': form_data['lastname'],
140 140 'active': form_data['active'],
141 141 'extern_type': form_data['extern_type'],
142 142 'extern_name': form_data['extern_name'],
143 143 'admin': False,
144 144 'cur_user': cur_user
145 145 }
146 146
147 147 if 'create_repo_group' in form_data:
148 148 user_data['create_repo_group'] = str2bool(
149 149 form_data.get('create_repo_group'))
150 150
151 151 try:
152 152 if form_data.get('password_change'):
153 153 user_data['force_password_change'] = True
154 154 return UserModel().create_or_update(**user_data)
155 155 except Exception:
156 156 log.error(traceback.format_exc())
157 157 raise
158 158
159 159 def update_user(self, user, skip_attrs=None, **kwargs):
160 160 from rhodecode.lib.auth import get_crypt_password
161 161
162 162 user = self._get_user(user)
163 163 if user.username == User.DEFAULT_USER:
164 164 raise DefaultUserException(
165 165 "You can't edit this user (`%(username)s`) since it's "
166 166 "crucial for entire application" % {
167 167 'username': user.username})
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
251 251 if not cur_user:
252 252 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
253 253
254 254 from rhodecode.lib.auth import (
255 255 get_crypt_password, check_password, generate_auth_token)
256 256 from rhodecode.lib.hooks_base import (
257 257 log_create_user, check_allowed_create_user)
258 258
259 259 def _password_change(new_user, password):
260 260 old_password = new_user.password or ''
261 261 # empty password
262 262 if not old_password:
263 263 return False
264 264
265 265 # password check is only needed for RhodeCode internal auth calls
266 266 # in case it's a plugin we don't care
267 267 if not plugin:
268 268
269 269 # first check if we gave crypted password back, and if it
270 270 # matches it's not password change
271 271 if new_user.password == password:
272 272 return False
273 273
274 274 password_match = check_password(password, old_password)
275 275 if not password_match:
276 276 return True
277 277
278 278 return False
279 279
280 280 # read settings on default personal repo group creation
281 281 if create_repo_group is None:
282 282 default_create_repo_group = RepoGroupModel()\
283 283 .get_default_create_personal_repo_group()
284 284 create_repo_group = default_create_repo_group
285 285
286 286 user_data = {
287 287 'username': username,
288 288 'password': password,
289 289 'email': email,
290 290 'firstname': firstname,
291 291 'lastname': lastname,
292 292 'active': active,
293 293 'admin': admin
294 294 }
295 295
296 296 if updating_user_id:
297 297 log.debug('Checking for existing account in RhodeCode '
298 298 'database with user_id `%s` ' % (updating_user_id,))
299 299 user = User.get(updating_user_id)
300 300 else:
301 301 log.debug('Checking for existing account in RhodeCode '
302 302 'database with username `%s` ' % (username,))
303 303 user = User.get_by_username(username, case_insensitive=True)
304 304
305 305 if user is None:
306 306 # we check internal flag if this method is actually allowed to
307 307 # create new user
308 308 if not allow_to_create_user:
309 309 msg = ('Method wants to create new user, but it is not '
310 310 'allowed to do so')
311 311 log.warning(msg)
312 312 raise NotAllowedToCreateUserError(msg)
313 313
314 314 log.debug('Creating new user %s', username)
315 315
316 316 # only if we create user that is active
317 317 new_active_user = active
318 318 if new_active_user and strict_creation_check:
319 319 # raises UserCreationError if it's not allowed for any reason to
320 320 # create new active user, this also executes pre-create hooks
321 321 check_allowed_create_user(user_data, cur_user, strict_check=True)
322 322 events.trigger(events.UserPreCreate(user_data))
323 323 new_user = User()
324 324 edit = False
325 325 else:
326 326 log.debug('updating user %s', username)
327 327 events.trigger(events.UserPreUpdate(user, user_data))
328 328 new_user = user
329 329 edit = True
330 330
331 331 # we're not allowed to edit default user
332 332 if user.username == User.DEFAULT_USER:
333 333 raise DefaultUserException(
334 334 "You can't edit this user (`%(username)s`) since it's "
335 335 "crucial for entire application"
336 336 % {'username': user.username})
337 337
338 338 # inject special attribute that will tell us if User is new or old
339 339 new_user.is_new_user = not edit
340 340 # for users that didn's specify auth type, we use RhodeCode built in
341 341 from rhodecode.authentication.plugins import auth_rhodecode
342 342 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
343 343 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
344 344
345 345 try:
346 346 new_user.username = username
347 347 new_user.admin = admin
348 348 new_user.email = email
349 349 new_user.active = active
350 350 new_user.extern_name = safe_unicode(extern_name)
351 351 new_user.extern_type = safe_unicode(extern_type)
352 352 new_user.name = firstname
353 353 new_user.lastname = lastname
354 354
355 355 # set password only if creating an user or password is changed
356 356 if not edit or _password_change(new_user, password):
357 357 reason = 'new password' if edit else 'new user'
358 358 log.debug('Updating password reason=>%s', reason)
359 359 new_user.password = get_crypt_password(password) if password else None
360 360
361 361 if force_password_change:
362 362 new_user.update_userdata(force_password_change=True)
363 363 if language:
364 364 new_user.update_userdata(language=language)
365 365 new_user.update_userdata(notification_status=True)
366 366
367 367 self.sa.add(new_user)
368 368
369 369 if not edit and create_repo_group:
370 370 RepoGroupModel().create_personal_repo_group(
371 371 new_user, commit_early=False)
372 372
373 373 if not edit:
374 374 # add the RSS token
375 375 AuthTokenModel().create(username,
376 376 description=u'Generated feed token',
377 377 role=AuthTokenModel.cls.ROLE_FEED)
378 378 kwargs = new_user.get_dict()
379 379 # backward compat, require api_keys present
380 380 kwargs['api_keys'] = kwargs['auth_tokens']
381 381 log_create_user(created_by=cur_user, **kwargs)
382 382 events.trigger(events.UserPostCreate(user_data))
383 383 return new_user
384 384 except (DatabaseError,):
385 385 log.error(traceback.format_exc())
386 386 raise
387 387
388 388 def create_registration(self, form_data):
389 389 from rhodecode.model.notification import NotificationModel
390 390 from rhodecode.model.notification import EmailNotificationModel
391 391
392 392 try:
393 393 form_data['admin'] = False
394 394 form_data['extern_name'] = 'rhodecode'
395 395 form_data['extern_type'] = 'rhodecode'
396 396 new_user = self.create(form_data)
397 397
398 398 self.sa.add(new_user)
399 399 self.sa.flush()
400 400
401 401 user_data = new_user.get_dict()
402 402 kwargs = {
403 403 # use SQLALCHEMY safe dump of user data
404 404 'user': AttributeDict(user_data),
405 405 'date': datetime.datetime.now()
406 406 }
407 407 notification_type = EmailNotificationModel.TYPE_REGISTRATION
408 408 # pre-generate the subject for notification itself
409 409 (subject,
410 410 _h, _e, # we don't care about those
411 411 body_plaintext) = EmailNotificationModel().render_email(
412 412 notification_type, **kwargs)
413 413
414 414 # create notification objects, and emails
415 415 NotificationModel().create(
416 416 created_by=new_user,
417 417 notification_subject=subject,
418 418 notification_body=body_plaintext,
419 419 notification_type=notification_type,
420 420 recipients=None, # all admins
421 421 email_kwargs=kwargs,
422 422 )
423 423
424 424 return new_user
425 425 except Exception:
426 426 log.error(traceback.format_exc())
427 427 raise
428 428
429 429 def _handle_user_repos(self, username, repositories, handle_mode=None):
430 430 _superadmin = self.cls.get_first_super_admin()
431 431 left_overs = True
432 432
433 433 from rhodecode.model.repo import RepoModel
434 434
435 435 if handle_mode == 'detach':
436 436 for obj in repositories:
437 437 obj.user = _superadmin
438 438 # set description we know why we super admin now owns
439 439 # additional repositories that were orphaned !
440 440 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
441 441 self.sa.add(obj)
442 442 left_overs = False
443 443 elif handle_mode == 'delete':
444 444 for obj in repositories:
445 445 RepoModel().delete(obj, forks='detach')
446 446 left_overs = False
447 447
448 448 # if nothing is done we have left overs left
449 449 return left_overs
450 450
451 451 def _handle_user_repo_groups(self, username, repository_groups,
452 452 handle_mode=None):
453 453 _superadmin = self.cls.get_first_super_admin()
454 454 left_overs = True
455 455
456 456 from rhodecode.model.repo_group import RepoGroupModel
457 457
458 458 if handle_mode == 'detach':
459 459 for r in repository_groups:
460 460 r.user = _superadmin
461 461 # set description we know why we super admin now owns
462 462 # additional repositories that were orphaned !
463 463 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
464 464 self.sa.add(r)
465 465 left_overs = False
466 466 elif handle_mode == 'delete':
467 467 for r in repository_groups:
468 468 RepoGroupModel().delete(r)
469 469 left_overs = False
470 470
471 471 # if nothing is done we have left overs left
472 472 return left_overs
473 473
474 474 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
475 475 _superadmin = self.cls.get_first_super_admin()
476 476 left_overs = True
477 477
478 478 from rhodecode.model.user_group import UserGroupModel
479 479
480 480 if handle_mode == 'detach':
481 481 for r in user_groups:
482 482 for user_user_group_to_perm in r.user_user_group_to_perm:
483 483 if user_user_group_to_perm.user.username == username:
484 484 user_user_group_to_perm.user = _superadmin
485 485 r.user = _superadmin
486 486 # set description we know why we super admin now owns
487 487 # additional repositories that were orphaned !
488 488 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
489 489 self.sa.add(r)
490 490 left_overs = False
491 491 elif handle_mode == 'delete':
492 492 for r in user_groups:
493 493 UserGroupModel().delete(r)
494 494 left_overs = False
495 495
496 496 # if nothing is done we have left overs left
497 497 return left_overs
498 498
499 499 def delete(self, user, cur_user=None, handle_repos=None,
500 500 handle_repo_groups=None, handle_user_groups=None):
501 501 if not cur_user:
502 502 cur_user = getattr(
503 503 get_current_rhodecode_user(), 'username', None)
504 504 user = self._get_user(user)
505 505
506 506 try:
507 507 if user.username == User.DEFAULT_USER:
508 508 raise DefaultUserException(
509 509 u"You can't remove this user since it's"
510 510 u" crucial for entire application")
511 511
512 512 left_overs = self._handle_user_repos(
513 513 user.username, user.repositories, handle_repos)
514 514 if left_overs and user.repositories:
515 515 repos = [x.repo_name for x in user.repositories]
516 516 raise UserOwnsReposException(
517 517 u'user "%(username)s" still owns %(len_repos)s repositories and cannot be '
518 518 u'removed. Switch owners or remove those repositories:%(list_repos)s'
519 519 % {'username': user.username, 'len_repos': len(repos),
520 520 'list_repos': ', '.join(repos)})
521 521
522 522 left_overs = self._handle_user_repo_groups(
523 523 user.username, user.repository_groups, handle_repo_groups)
524 524 if left_overs and user.repository_groups:
525 525 repo_groups = [x.group_name for x in user.repository_groups]
526 526 raise UserOwnsRepoGroupsException(
527 527 u'user "%(username)s" still owns %(len_repo_groups)s repository groups and cannot be '
528 528 u'removed. Switch owners or remove those repository groups:%(list_repo_groups)s'
529 529 % {'username': user.username, 'len_repo_groups': len(repo_groups),
530 530 'list_repo_groups': ', '.join(repo_groups)})
531 531
532 532 left_overs = self._handle_user_user_groups(
533 533 user.username, user.user_groups, handle_user_groups)
534 534 if left_overs and user.user_groups:
535 535 user_groups = [x.users_group_name for x in user.user_groups]
536 536 raise UserOwnsUserGroupsException(
537 537 u'user "%s" still owns %s user groups and cannot be '
538 538 u'removed. Switch owners or remove those user groups:%s'
539 539 % (user.username, len(user_groups), ', '.join(user_groups)))
540 540
541 541 # we might change the user data with detach/delete, make sure
542 542 # the object is marked as expired before actually deleting !
543 543 self.sa.expire(user)
544 544 self.sa.delete(user)
545 545 from rhodecode.lib.hooks_base import log_delete_user
546 546 log_delete_user(deleted_by=cur_user, **user.get_dict())
547 547 except Exception:
548 548 log.error(traceback.format_exc())
549 549 raise
550 550
551 551 def reset_password_link(self, data, pwd_reset_url):
552 552 from rhodecode.lib.celerylib import tasks, run_task
553 553 from rhodecode.model.notification import EmailNotificationModel
554 554 user_email = data['email']
555 555 try:
556 556 user = User.get_by_email(user_email)
557 557 if user:
558 558 log.debug('password reset user found %s', user)
559 559
560 560 email_kwargs = {
561 561 'password_reset_url': pwd_reset_url,
562 562 'user': user,
563 563 'email': user_email,
564 564 'date': datetime.datetime.now()
565 565 }
566 566
567 567 (subject, headers, email_body,
568 568 email_body_plaintext) = EmailNotificationModel().render_email(
569 569 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
570 570
571 571 recipients = [user_email]
572 572
573 573 action_logger_generic(
574 574 'sending password reset email to user: {}'.format(
575 575 user), namespace='security.password_reset')
576 576
577 577 run_task(tasks.send_email, recipients, subject,
578 578 email_body_plaintext, email_body)
579 579
580 580 else:
581 581 log.debug("password reset email %s not found", user_email)
582 582 except Exception:
583 583 log.error(traceback.format_exc())
584 584 return False
585 585
586 586 return True
587 587
588 588 def reset_password(self, data):
589 589 from rhodecode.lib.celerylib import tasks, run_task
590 590 from rhodecode.model.notification import EmailNotificationModel
591 591 from rhodecode.lib import auth
592 592 user_email = data['email']
593 593 pre_db = True
594 594 try:
595 595 user = User.get_by_email(user_email)
596 596 new_passwd = auth.PasswordGenerator().gen_password(
597 597 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
598 598 if user:
599 599 user.password = auth.get_crypt_password(new_passwd)
600 600 # also force this user to reset his password !
601 601 user.update_userdata(force_password_change=True)
602 602
603 603 Session().add(user)
604 604
605 605 # now delete the token in question
606 606 UserApiKeys = AuthTokenModel.cls
607 607 UserApiKeys().query().filter(
608 608 UserApiKeys.api_key == data['token']).delete()
609 609
610 610 Session().commit()
611 611 log.info('successfully reset password for `%s`', user_email)
612 612
613 613 if new_passwd is None:
614 614 raise Exception('unable to generate new password')
615 615
616 616 pre_db = False
617 617
618 618 email_kwargs = {
619 619 'new_password': new_passwd,
620 620 'user': user,
621 621 'email': user_email,
622 622 'date': datetime.datetime.now()
623 623 }
624 624
625 625 (subject, headers, email_body,
626 626 email_body_plaintext) = EmailNotificationModel().render_email(
627 627 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
628 628 **email_kwargs)
629 629
630 630 recipients = [user_email]
631 631
632 632 action_logger_generic(
633 633 'sent new password to user: {} with email: {}'.format(
634 634 user, user_email), namespace='security.password_reset')
635 635
636 636 run_task(tasks.send_email, recipients, subject,
637 637 email_body_plaintext, email_body)
638 638
639 639 except Exception:
640 640 log.error('Failed to update user password')
641 641 log.error(traceback.format_exc())
642 642 if pre_db:
643 643 # we rollback only if local db stuff fails. If it goes into
644 644 # run_task, we're pass rollback state this wouldn't work then
645 645 Session().rollback()
646 646
647 647 return True
648 648
649 649 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
650 650 """
651 651 Fetches auth_user by user_id,or api_key if present.
652 652 Fills auth_user attributes with those taken from database.
653 653 Additionally set's is_authenitated if lookup fails
654 654 present in database
655 655
656 656 :param auth_user: instance of user to set attributes
657 657 :param user_id: user id to fetch by
658 658 :param api_key: api key to fetch by
659 659 :param username: username to fetch by
660 660 """
661 661 if user_id is None and api_key is None and username is None:
662 662 raise Exception('You need to pass user_id, api_key or username')
663 663
664 664 log.debug(
665 665 'AuthUser: fill data execution based on: '
666 666 'user_id:%s api_key:%s username:%s', user_id, api_key, username)
667 667 try:
668 668 dbuser = None
669 669 if user_id:
670 670 dbuser = self.get(user_id)
671 671 elif api_key:
672 672 dbuser = self.get_by_auth_token(api_key)
673 673 elif username:
674 674 dbuser = self.get_by_username(username)
675 675
676 676 if not dbuser:
677 677 log.warning(
678 678 'Unable to lookup user by id:%s api_key:%s username:%s',
679 679 user_id, api_key, username)
680 680 return False
681 681 if not dbuser.active:
682 682 log.debug('User `%s:%s` is inactive, skipping fill data',
683 683 username, user_id)
684 684 return False
685 685
686 686 log.debug('AuthUser: filling found user:%s data', dbuser)
687 687 user_data = dbuser.get_dict()
688 688
689 689 user_data.update({
690 690 # set explicit the safe escaped values
691 691 'first_name': dbuser.first_name,
692 692 'last_name': dbuser.last_name,
693 693 })
694 694
695 695 for k, v in user_data.items():
696 696 # properties of auth user we dont update
697 697 if k not in ['auth_tokens', 'permissions']:
698 698 setattr(auth_user, k, v)
699 699
700 # few extras
701 setattr(auth_user, 'feed_token', dbuser.feed_token)
702 700 except Exception:
703 701 log.error(traceback.format_exc())
704 702 auth_user.is_authenticated = False
705 703 return False
706 704
707 705 return True
708 706
709 707 def has_perm(self, user, perm):
710 708 perm = self._get_perm(perm)
711 709 user = self._get_user(user)
712 710
713 711 return UserToPerm.query().filter(UserToPerm.user == user)\
714 712 .filter(UserToPerm.permission == perm).scalar() is not None
715 713
716 714 def grant_perm(self, user, perm):
717 715 """
718 716 Grant user global permissions
719 717
720 718 :param user:
721 719 :param perm:
722 720 """
723 721 user = self._get_user(user)
724 722 perm = self._get_perm(perm)
725 723 # if this permission is already granted skip it
726 724 _perm = UserToPerm.query()\
727 725 .filter(UserToPerm.user == user)\
728 726 .filter(UserToPerm.permission == perm)\
729 727 .scalar()
730 728 if _perm:
731 729 return
732 730 new = UserToPerm()
733 731 new.user = user
734 732 new.permission = perm
735 733 self.sa.add(new)
736 734 return new
737 735
738 736 def revoke_perm(self, user, perm):
739 737 """
740 738 Revoke users global permissions
741 739
742 740 :param user:
743 741 :param perm:
744 742 """
745 743 user = self._get_user(user)
746 744 perm = self._get_perm(perm)
747 745
748 746 obj = UserToPerm.query()\
749 747 .filter(UserToPerm.user == user)\
750 748 .filter(UserToPerm.permission == perm)\
751 749 .scalar()
752 750 if obj:
753 751 self.sa.delete(obj)
754 752
755 753 def add_extra_email(self, user, email):
756 754 """
757 755 Adds email address to UserEmailMap
758 756
759 757 :param user:
760 758 :param email:
761 759 """
762 760
763 761 user = self._get_user(user)
764 762
765 763 obj = UserEmailMap()
766 764 obj.user = user
767 765 obj.email = email
768 766 self.sa.add(obj)
769 767 return obj
770 768
771 769 def delete_extra_email(self, user, email_id):
772 770 """
773 771 Removes email address from UserEmailMap
774 772
775 773 :param user:
776 774 :param email_id:
777 775 """
778 776 user = self._get_user(user)
779 777 obj = UserEmailMap.query().get(email_id)
780 778 if obj and obj.user_id == user.user_id:
781 779 self.sa.delete(obj)
782 780
783 781 def parse_ip_range(self, ip_range):
784 782 ip_list = []
785 783
786 784 def make_unique(value):
787 785 seen = []
788 786 return [c for c in value if not (c in seen or seen.append(c))]
789 787
790 788 # firsts split by commas
791 789 for ip_range in ip_range.split(','):
792 790 if not ip_range:
793 791 continue
794 792 ip_range = ip_range.strip()
795 793 if '-' in ip_range:
796 794 start_ip, end_ip = ip_range.split('-', 1)
797 795 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
798 796 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
799 797 parsed_ip_range = []
800 798
801 799 for index in xrange(int(start_ip), int(end_ip) + 1):
802 800 new_ip = ipaddress.ip_address(index)
803 801 parsed_ip_range.append(str(new_ip))
804 802 ip_list.extend(parsed_ip_range)
805 803 else:
806 804 ip_list.append(ip_range)
807 805
808 806 return make_unique(ip_list)
809 807
810 808 def add_extra_ip(self, user, ip, description=None):
811 809 """
812 810 Adds ip address to UserIpMap
813 811
814 812 :param user:
815 813 :param ip:
816 814 """
817 815
818 816 user = self._get_user(user)
819 817 obj = UserIpMap()
820 818 obj.user = user
821 819 obj.ip_addr = ip
822 820 obj.description = description
823 821 self.sa.add(obj)
824 822 return obj
825 823
826 824 def delete_extra_ip(self, user, ip_id):
827 825 """
828 826 Removes ip address from UserIpMap
829 827
830 828 :param user:
831 829 :param ip_id:
832 830 """
833 831 user = self._get_user(user)
834 832 obj = UserIpMap.query().get(ip_id)
835 833 if obj and obj.user_id == user.user_id:
836 834 self.sa.delete(obj)
837 835
838 836 def get_accounts_in_creation_order(self, current_user=None):
839 837 """
840 838 Get accounts in order of creation for deactivation for license limits
841 839
842 840 pick currently logged in user, and append to the list in position 0
843 841 pick all super-admins in order of creation date and add it to the list
844 842 pick all other accounts in order of creation and add it to the list.
845 843
846 844 Based on that list, the last accounts can be disabled as they are
847 845 created at the end and don't include any of the super admins as well
848 846 as the current user.
849 847
850 848 :param current_user: optionally current user running this operation
851 849 """
852 850
853 851 if not current_user:
854 852 current_user = get_current_rhodecode_user()
855 853 active_super_admins = [
856 854 x.user_id for x in User.query()
857 855 .filter(User.user_id != current_user.user_id)
858 856 .filter(User.active == true())
859 857 .filter(User.admin == true())
860 858 .order_by(User.created_on.asc())]
861 859
862 860 active_regular_users = [
863 861 x.user_id for x in User.query()
864 862 .filter(User.user_id != current_user.user_id)
865 863 .filter(User.active == true())
866 864 .filter(User.admin == false())
867 865 .order_by(User.created_on.asc())]
868 866
869 867 list_of_accounts = [current_user.user_id]
870 868 list_of_accounts += active_super_admins
871 869 list_of_accounts += active_regular_users
872 870
873 871 return list_of_accounts
874 872
875 873 def deactivate_last_users(self, expected_users, current_user=None):
876 874 """
877 875 Deactivate accounts that are over the license limits.
878 876 Algorithm of which accounts to disabled is based on the formula:
879 877
880 878 Get current user, then super admins in creation order, then regular
881 879 active users in creation order.
882 880
883 881 Using that list we mark all accounts from the end of it as inactive.
884 882 This way we block only latest created accounts.
885 883
886 884 :param expected_users: list of users in special order, we deactivate
887 885 the end N ammoun of users from that list
888 886 """
889 887
890 888 list_of_accounts = self.get_accounts_in_creation_order(
891 889 current_user=current_user)
892 890
893 891 for acc_id in list_of_accounts[expected_users + 1:]:
894 892 user = User.get(acc_id)
895 893 log.info('Deactivating account %s for license unlock', user)
896 894 user.active = False
897 895 Session().add(user)
898 896 Session().commit()
899 897
900 898 return
901 899
902 900 def get_user_log(self, user, filter_term):
903 901 user_log = UserLog.query()\
904 902 .filter(or_(UserLog.user_id == user.user_id,
905 903 UserLog.username == user.username))\
906 904 .options(joinedload(UserLog.user))\
907 905 .options(joinedload(UserLog.repository))\
908 906 .order_by(UserLog.action_date.desc())
909 907
910 908 user_log = user_log_filter(user_log, filter_term)
911 909 return user_log
General Comments 0
You need to be logged in to leave comments. Login now