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