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