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