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