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