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