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