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