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