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