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