##// END OF EJS Templates
added some more logging into get_container_username function
marcink -
r3172:264d9c93 beta
parent child Browse files
Show More
@@ -1,982 +1,984 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.auth
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 authentication and permission libraries
7 7
8 8 :created_on: Apr 4, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import random
27 27 import logging
28 28 import traceback
29 29 import hashlib
30 30
31 31 from tempfile import _RandomNameSequence
32 32 from decorator import decorator
33 33
34 34 from pylons import config, url, request
35 35 from pylons.controllers.util import abort, redirect
36 36 from pylons.i18n.translation import _
37 37
38 38 from rhodecode import __platform__, is_windows, is_unix
39 39 from rhodecode.model.meta import Session
40 40
41 41 from rhodecode.lib.utils2 import str2bool, safe_unicode
42 42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
43 43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
44 44 from rhodecode.lib.auth_ldap import AuthLdap
45 45
46 46 from rhodecode.model import meta
47 47 from rhodecode.model.user import UserModel
48 48 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
49 49 from rhodecode.lib.caching_query import FromCache
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 class PasswordGenerator(object):
55 55 """
56 56 This is a simple class for generating password from different sets of
57 57 characters
58 58 usage::
59 59
60 60 passwd_gen = PasswordGenerator()
61 61 #print 8-letter password containing only big and small letters
62 62 of alphabet
63 63 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 64 """
65 65 ALPHABETS_NUM = r'''1234567890'''
66 66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
67 67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
68 68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
69 69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
70 70 + ALPHABETS_NUM + ALPHABETS_SPECIAL
71 71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
72 72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
73 73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
74 74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
75 75
76 76 def __init__(self, passwd=''):
77 77 self.passwd = passwd
78 78
79 79 def gen_password(self, length, type_=None):
80 80 if type_ is None:
81 81 type_ = self.ALPHABETS_FULL
82 82 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
83 83 return self.passwd
84 84
85 85
86 86 class RhodeCodeCrypto(object):
87 87
88 88 @classmethod
89 89 def hash_string(cls, str_):
90 90 """
91 91 Cryptographic function used for password hashing based on pybcrypt
92 92 or pycrypto in windows
93 93
94 94 :param password: password to hash
95 95 """
96 96 if is_windows:
97 97 from hashlib import sha256
98 98 return sha256(str_).hexdigest()
99 99 elif is_unix:
100 100 import bcrypt
101 101 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
102 102 else:
103 103 raise Exception('Unknown or unsupported platform %s' \
104 104 % __platform__)
105 105
106 106 @classmethod
107 107 def hash_check(cls, password, hashed):
108 108 """
109 109 Checks matching password with it's hashed value, runs different
110 110 implementation based on platform it runs on
111 111
112 112 :param password: password
113 113 :param hashed: password in hashed form
114 114 """
115 115
116 116 if is_windows:
117 117 from hashlib import sha256
118 118 return sha256(password).hexdigest() == hashed
119 119 elif is_unix:
120 120 import bcrypt
121 121 return bcrypt.hashpw(password, hashed) == hashed
122 122 else:
123 123 raise Exception('Unknown or unsupported platform %s' \
124 124 % __platform__)
125 125
126 126
127 127 def get_crypt_password(password):
128 128 return RhodeCodeCrypto.hash_string(password)
129 129
130 130
131 131 def check_password(password, hashed):
132 132 return RhodeCodeCrypto.hash_check(password, hashed)
133 133
134 134
135 135 def generate_api_key(str_, salt=None):
136 136 """
137 137 Generates API KEY from given string
138 138
139 139 :param str_:
140 140 :param salt:
141 141 """
142 142
143 143 if salt is None:
144 144 salt = _RandomNameSequence().next()
145 145
146 146 return hashlib.sha1(str_ + salt).hexdigest()
147 147
148 148
149 149 def authfunc(environ, username, password):
150 150 """
151 151 Dummy authentication wrapper function used in Mercurial and Git for
152 152 access control.
153 153
154 154 :param environ: needed only for using in Basic auth
155 155 """
156 156 return authenticate(username, password)
157 157
158 158
159 159 def authenticate(username, password):
160 160 """
161 161 Authentication function used for access control,
162 162 firstly checks for db authentication then if ldap is enabled for ldap
163 163 authentication, also creates ldap user if not in database
164 164
165 165 :param username: username
166 166 :param password: password
167 167 """
168 168
169 169 user_model = UserModel()
170 170 user = User.get_by_username(username)
171 171
172 172 log.debug('Authenticating user using RhodeCode account')
173 173 if user is not None and not user.ldap_dn:
174 174 if user.active:
175 175 if user.username == 'default' and user.active:
176 176 log.info('user %s authenticated correctly as anonymous user' %
177 177 username)
178 178 return True
179 179
180 180 elif user.username == username and check_password(password,
181 181 user.password):
182 182 log.info('user %s authenticated correctly' % username)
183 183 return True
184 184 else:
185 185 log.warning('user %s tried auth but is disabled' % username)
186 186
187 187 else:
188 188 log.debug('Regular authentication failed')
189 189 user_obj = User.get_by_username(username, case_insensitive=True)
190 190
191 191 if user_obj is not None and not user_obj.ldap_dn:
192 192 log.debug('this user already exists as non ldap')
193 193 return False
194 194
195 195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
196 196 #======================================================================
197 197 # FALLBACK TO LDAP AUTH IF ENABLE
198 198 #======================================================================
199 199 if str2bool(ldap_settings.get('ldap_active')):
200 200 log.debug("Authenticating user using ldap")
201 201 kwargs = {
202 202 'server': ldap_settings.get('ldap_host', ''),
203 203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
204 204 'port': ldap_settings.get('ldap_port'),
205 205 'bind_dn': ldap_settings.get('ldap_dn_user'),
206 206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
207 207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
208 208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
209 209 'ldap_filter': ldap_settings.get('ldap_filter'),
210 210 'search_scope': ldap_settings.get('ldap_search_scope'),
211 211 'attr_login': ldap_settings.get('ldap_attr_login'),
212 212 'ldap_version': 3,
213 213 }
214 214 log.debug('Checking for ldap authentication')
215 215 try:
216 216 aldap = AuthLdap(**kwargs)
217 217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
218 218 password)
219 219 log.debug('Got ldap DN response %s' % user_dn)
220 220
221 221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
222 222 .get(k), [''])[0]
223 223
224 224 user_attrs = {
225 225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
226 226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
227 227 'email': get_ldap_attr('ldap_attr_email'),
228 228 }
229 229
230 230 # don't store LDAP password since we don't need it. Override
231 231 # with some random generated password
232 232 _password = PasswordGenerator().gen_password(length=8)
233 233 # create this user on the fly if it doesn't exist in rhodecode
234 234 # database
235 235 if user_model.create_ldap(username, _password, user_dn,
236 236 user_attrs):
237 237 log.info('created new ldap user %s' % username)
238 238
239 239 Session().commit()
240 240 return True
241 241 except (LdapUsernameError, LdapPasswordError,):
242 242 pass
243 243 except (Exception,):
244 244 log.error(traceback.format_exc())
245 245 pass
246 246 return False
247 247
248 248
249 249 def login_container_auth(username):
250 250 user = User.get_by_username(username)
251 251 if user is None:
252 252 user_attrs = {
253 253 'name': username,
254 254 'lastname': None,
255 255 'email': None,
256 256 }
257 257 user = UserModel().create_for_container_auth(username, user_attrs)
258 258 if not user:
259 259 return None
260 260 log.info('User %s was created by container authentication' % username)
261 261
262 262 if not user.active:
263 263 return None
264 264
265 265 user.update_lastlogin()
266 266 Session().commit()
267 267
268 268 log.debug('User %s is now logged in by container authentication',
269 269 user.username)
270 270 return user
271 271
272 272
273 273 def get_container_username(environ, config):
274 274 username = None
275 275
276 276 if str2bool(config.get('container_auth_enabled', False)):
277 277 from paste.httpheaders import REMOTE_USER
278 278 username = REMOTE_USER(environ)
279 log.debug('extracted REMOTE_USER:%s' % (username))
279 280
280 281 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
281 282 username = environ.get('HTTP_X_FORWARDED_USER')
283 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
282 284
283 285 if username:
284 286 # Removing realm and domain from username
285 287 username = username.partition('@')[0]
286 288 username = username.rpartition('\\')[2]
287 log.debug('Received username %s from container' % username)
289 log.debug('Received username %s from container' % username)
288 290
289 291 return username
290 292
291 293
292 294 class CookieStoreWrapper(object):
293 295
294 296 def __init__(self, cookie_store):
295 297 self.cookie_store = cookie_store
296 298
297 299 def __repr__(self):
298 300 return 'CookieStore<%s>' % (self.cookie_store)
299 301
300 302 def get(self, key, other=None):
301 303 if isinstance(self.cookie_store, dict):
302 304 return self.cookie_store.get(key, other)
303 305 elif isinstance(self.cookie_store, AuthUser):
304 306 return self.cookie_store.__dict__.get(key, other)
305 307
306 308
307 309 class AuthUser(object):
308 310 """
309 311 A simple object that handles all attributes of user in RhodeCode
310 312
311 313 It does lookup based on API key,given user, or user present in session
312 314 Then it fills all required information for such user. It also checks if
313 315 anonymous access is enabled and if so, it returns default user as logged
314 316 in
315 317 """
316 318
317 319 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
318 320
319 321 self.user_id = user_id
320 322 self.api_key = None
321 323 self.username = username
322 324 self.ip_addr = ip_addr
323 325
324 326 self.name = ''
325 327 self.lastname = ''
326 328 self.email = ''
327 329 self.is_authenticated = False
328 330 self.admin = False
329 331 self.inherit_default_permissions = False
330 332 self.permissions = {}
331 333 self._api_key = api_key
332 334 self.propagate_data()
333 335 self._instance = None
334 336
335 337 def propagate_data(self):
336 338 user_model = UserModel()
337 339 self.anonymous_user = User.get_by_username('default', cache=True)
338 340 is_user_loaded = False
339 341
340 342 # try go get user by api key
341 343 if self._api_key and self._api_key != self.anonymous_user.api_key:
342 344 log.debug('Auth User lookup by API KEY %s' % self._api_key)
343 345 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
344 346 # lookup by userid
345 347 elif (self.user_id is not None and
346 348 self.user_id != self.anonymous_user.user_id):
347 349 log.debug('Auth User lookup by USER ID %s' % self.user_id)
348 350 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
349 351 # lookup by username
350 352 elif self.username and \
351 353 str2bool(config.get('container_auth_enabled', False)):
352 354
353 355 log.debug('Auth User lookup by USER NAME %s' % self.username)
354 356 dbuser = login_container_auth(self.username)
355 357 if dbuser is not None:
356 358 log.debug('filling all attributes to object')
357 359 for k, v in dbuser.get_dict().items():
358 360 setattr(self, k, v)
359 361 self.set_authenticated()
360 362 is_user_loaded = True
361 363 else:
362 364 log.debug('No data in %s that could been used to log in' % self)
363 365
364 366 if not is_user_loaded:
365 367 # if we cannot authenticate user try anonymous
366 368 if self.anonymous_user.active is True:
367 369 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
368 370 # then we set this user is logged in
369 371 self.is_authenticated = True
370 372 else:
371 373 self.user_id = None
372 374 self.username = None
373 375 self.is_authenticated = False
374 376
375 377 if not self.username:
376 378 self.username = 'None'
377 379
378 380 log.debug('Auth User is now %s' % self)
379 381 user_model.fill_perms(self)
380 382
381 383 @property
382 384 def is_admin(self):
383 385 return self.admin
384 386
385 387 @property
386 388 def ip_allowed(self):
387 389 """
388 390 Checks if ip_addr used in constructor is allowed from defined list of
389 391 allowed ip_addresses for user
390 392
391 393 :returns: boolean, True if ip is in allowed ip range
392 394 """
393 395 #check IP
394 396 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
395 397 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
396 398 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
397 399 return True
398 400 else:
399 401 log.info('Access for IP:%s forbidden, '
400 402 'not in %s' % (self.ip_addr, allowed_ips))
401 403 return False
402 404
403 405 def __repr__(self):
404 406 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
405 407 self.is_authenticated)
406 408
407 409 def set_authenticated(self, authenticated=True):
408 410 if self.user_id != self.anonymous_user.user_id:
409 411 self.is_authenticated = authenticated
410 412
411 413 def get_cookie_store(self):
412 414 return {'username': self.username,
413 415 'user_id': self.user_id,
414 416 'is_authenticated': self.is_authenticated}
415 417
416 418 @classmethod
417 419 def from_cookie_store(cls, cookie_store):
418 420 """
419 421 Creates AuthUser from a cookie store
420 422
421 423 :param cls:
422 424 :param cookie_store:
423 425 """
424 426 user_id = cookie_store.get('user_id')
425 427 username = cookie_store.get('username')
426 428 api_key = cookie_store.get('api_key')
427 429 return AuthUser(user_id, api_key, username)
428 430
429 431 @classmethod
430 432 def get_allowed_ips(cls, user_id, cache=False):
431 433 _set = set()
432 434 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
433 435 if cache:
434 436 user_ips = user_ips.options(FromCache("sql_cache_short",
435 437 "get_user_ips_%s" % user_id))
436 438 for ip in user_ips:
437 439 _set.add(ip.ip_addr)
438 440 return _set or set(['0.0.0.0/0'])
439 441
440 442
441 443 def set_available_permissions(config):
442 444 """
443 445 This function will propagate pylons globals with all available defined
444 446 permission given in db. We don't want to check each time from db for new
445 447 permissions since adding a new permission also requires application restart
446 448 ie. to decorate new views with the newly created permission
447 449
448 450 :param config: current pylons config instance
449 451
450 452 """
451 453 log.info('getting information about all available permissions')
452 454 try:
453 455 sa = meta.Session
454 456 all_perms = sa.query(Permission).all()
455 457 except Exception:
456 458 pass
457 459 finally:
458 460 meta.Session.remove()
459 461
460 462 config['available_permissions'] = [x.permission_name for x in all_perms]
461 463
462 464
463 465 #==============================================================================
464 466 # CHECK DECORATORS
465 467 #==============================================================================
466 468 class LoginRequired(object):
467 469 """
468 470 Must be logged in to execute this function else
469 471 redirect to login page
470 472
471 473 :param api_access: if enabled this checks only for valid auth token
472 474 and grants access based on valid token
473 475 """
474 476
475 477 def __init__(self, api_access=False):
476 478 self.api_access = api_access
477 479
478 480 def __call__(self, func):
479 481 return decorator(self.__wrapper, func)
480 482
481 483 def __wrapper(self, func, *fargs, **fkwargs):
482 484 cls = fargs[0]
483 485 user = cls.rhodecode_user
484 486 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
485 487
486 488 #check IP
487 489 ip_access_ok = True
488 490 if not user.ip_allowed:
489 491 from rhodecode.lib import helpers as h
490 492 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
491 493 category='warning')
492 494 ip_access_ok = False
493 495
494 496 api_access_ok = False
495 497 if self.api_access:
496 498 log.debug('Checking API KEY access for %s' % cls)
497 499 if user.api_key == request.GET.get('api_key'):
498 500 api_access_ok = True
499 501 else:
500 502 log.debug("API KEY token not valid")
501 503
502 504 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
503 505 if (user.is_authenticated or api_access_ok) and ip_access_ok:
504 506 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
505 507 log.info('user %s is authenticated and granted access to %s '
506 508 'using %s' % (user.username, loc, reason)
507 509 )
508 510 return func(*fargs, **fkwargs)
509 511 else:
510 512 log.warn('user %s NOT authenticated on func: %s' % (
511 513 user, loc)
512 514 )
513 515 p = url.current()
514 516
515 517 log.debug('redirecting to login page with %s' % p)
516 518 return redirect(url('login_home', came_from=p))
517 519
518 520
519 521 class NotAnonymous(object):
520 522 """
521 523 Must be logged in to execute this function else
522 524 redirect to login page"""
523 525
524 526 def __call__(self, func):
525 527 return decorator(self.__wrapper, func)
526 528
527 529 def __wrapper(self, func, *fargs, **fkwargs):
528 530 cls = fargs[0]
529 531 self.user = cls.rhodecode_user
530 532
531 533 log.debug('Checking if user is not anonymous @%s' % cls)
532 534
533 535 anonymous = self.user.username == 'default'
534 536
535 537 if anonymous:
536 538 p = url.current()
537 539
538 540 import rhodecode.lib.helpers as h
539 541 h.flash(_('You need to be a registered user to '
540 542 'perform this action'),
541 543 category='warning')
542 544 return redirect(url('login_home', came_from=p))
543 545 else:
544 546 return func(*fargs, **fkwargs)
545 547
546 548
547 549 class PermsDecorator(object):
548 550 """Base class for controller decorators"""
549 551
550 552 def __init__(self, *required_perms):
551 553 available_perms = config['available_permissions']
552 554 for perm in required_perms:
553 555 if perm not in available_perms:
554 556 raise Exception("'%s' permission is not defined" % perm)
555 557 self.required_perms = set(required_perms)
556 558 self.user_perms = None
557 559
558 560 def __call__(self, func):
559 561 return decorator(self.__wrapper, func)
560 562
561 563 def __wrapper(self, func, *fargs, **fkwargs):
562 564 cls = fargs[0]
563 565 self.user = cls.rhodecode_user
564 566 self.user_perms = self.user.permissions
565 567 log.debug('checking %s permissions %s for %s %s',
566 568 self.__class__.__name__, self.required_perms, cls, self.user)
567 569
568 570 if self.check_permissions():
569 571 log.debug('Permission granted for %s %s' % (cls, self.user))
570 572 return func(*fargs, **fkwargs)
571 573
572 574 else:
573 575 log.debug('Permission denied for %s %s' % (cls, self.user))
574 576 anonymous = self.user.username == 'default'
575 577
576 578 if anonymous:
577 579 p = url.current()
578 580
579 581 import rhodecode.lib.helpers as h
580 582 h.flash(_('You need to be a signed in to '
581 583 'view this page'),
582 584 category='warning')
583 585 return redirect(url('login_home', came_from=p))
584 586
585 587 else:
586 588 # redirect with forbidden ret code
587 589 return abort(403)
588 590
589 591 def check_permissions(self):
590 592 """Dummy function for overriding"""
591 593 raise Exception('You have to write this function in child class')
592 594
593 595
594 596 class HasPermissionAllDecorator(PermsDecorator):
595 597 """
596 598 Checks for access permission for all given predicates. All of them
597 599 have to be meet in order to fulfill the request
598 600 """
599 601
600 602 def check_permissions(self):
601 603 if self.required_perms.issubset(self.user_perms.get('global')):
602 604 return True
603 605 return False
604 606
605 607
606 608 class HasPermissionAnyDecorator(PermsDecorator):
607 609 """
608 610 Checks for access permission for any of given predicates. In order to
609 611 fulfill the request any of predicates must be meet
610 612 """
611 613
612 614 def check_permissions(self):
613 615 if self.required_perms.intersection(self.user_perms.get('global')):
614 616 return True
615 617 return False
616 618
617 619
618 620 class HasRepoPermissionAllDecorator(PermsDecorator):
619 621 """
620 622 Checks for access permission for all given predicates for specific
621 623 repository. All of them have to be meet in order to fulfill the request
622 624 """
623 625
624 626 def check_permissions(self):
625 627 repo_name = get_repo_slug(request)
626 628 try:
627 629 user_perms = set([self.user_perms['repositories'][repo_name]])
628 630 except KeyError:
629 631 return False
630 632 if self.required_perms.issubset(user_perms):
631 633 return True
632 634 return False
633 635
634 636
635 637 class HasRepoPermissionAnyDecorator(PermsDecorator):
636 638 """
637 639 Checks for access permission for any of given predicates for specific
638 640 repository. In order to fulfill the request any of predicates must be meet
639 641 """
640 642
641 643 def check_permissions(self):
642 644 repo_name = get_repo_slug(request)
643 645
644 646 try:
645 647 user_perms = set([self.user_perms['repositories'][repo_name]])
646 648 except KeyError:
647 649 return False
648 650
649 651 if self.required_perms.intersection(user_perms):
650 652 return True
651 653 return False
652 654
653 655
654 656 class HasReposGroupPermissionAllDecorator(PermsDecorator):
655 657 """
656 658 Checks for access permission for all given predicates for specific
657 659 repository. All of them have to be meet in order to fulfill the request
658 660 """
659 661
660 662 def check_permissions(self):
661 663 group_name = get_repos_group_slug(request)
662 664 try:
663 665 user_perms = set([self.user_perms['repositories_groups'][group_name]])
664 666 except KeyError:
665 667 return False
666 668 if self.required_perms.issubset(user_perms):
667 669 return True
668 670 return False
669 671
670 672
671 673 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
672 674 """
673 675 Checks for access permission for any of given predicates for specific
674 676 repository. In order to fulfill the request any of predicates must be meet
675 677 """
676 678
677 679 def check_permissions(self):
678 680 group_name = get_repos_group_slug(request)
679 681
680 682 try:
681 683 user_perms = set([self.user_perms['repositories_groups'][group_name]])
682 684 except KeyError:
683 685 return False
684 686 if self.required_perms.intersection(user_perms):
685 687 return True
686 688 return False
687 689
688 690
689 691 #==============================================================================
690 692 # CHECK FUNCTIONS
691 693 #==============================================================================
692 694 class PermsFunction(object):
693 695 """Base function for other check functions"""
694 696
695 697 def __init__(self, *perms):
696 698 available_perms = config['available_permissions']
697 699
698 700 for perm in perms:
699 701 if perm not in available_perms:
700 702 raise Exception("'%s' permission is not defined" % perm)
701 703 self.required_perms = set(perms)
702 704 self.user_perms = None
703 705 self.repo_name = None
704 706 self.group_name = None
705 707
706 708 def __call__(self, check_Location=''):
707 709 user = request.user
708 710 cls_name = self.__class__.__name__
709 711 check_scope = {
710 712 'HasPermissionAll': '',
711 713 'HasPermissionAny': '',
712 714 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
713 715 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
714 716 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
715 717 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
716 718 }.get(cls_name, '?')
717 719 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
718 720 self.required_perms, user, check_scope,
719 721 check_Location or 'unspecified location')
720 722 if not user:
721 723 log.debug('Empty request user')
722 724 return False
723 725 self.user_perms = user.permissions
724 726 if self.check_permissions():
725 727 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
726 728 check_Location or 'unspecified location')
727 729 return True
728 730
729 731 else:
730 732 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
731 733 check_Location or 'unspecified location')
732 734 return False
733 735
734 736 def check_permissions(self):
735 737 """Dummy function for overriding"""
736 738 raise Exception('You have to write this function in child class')
737 739
738 740
739 741 class HasPermissionAll(PermsFunction):
740 742 def check_permissions(self):
741 743 if self.required_perms.issubset(self.user_perms.get('global')):
742 744 return True
743 745 return False
744 746
745 747
746 748 class HasPermissionAny(PermsFunction):
747 749 def check_permissions(self):
748 750 if self.required_perms.intersection(self.user_perms.get('global')):
749 751 return True
750 752 return False
751 753
752 754
753 755 class HasRepoPermissionAll(PermsFunction):
754 756 def __call__(self, repo_name=None, check_Location=''):
755 757 self.repo_name = repo_name
756 758 return super(HasRepoPermissionAll, self).__call__(check_Location)
757 759
758 760 def check_permissions(self):
759 761 if not self.repo_name:
760 762 self.repo_name = get_repo_slug(request)
761 763
762 764 try:
763 765 self._user_perms = set(
764 766 [self.user_perms['repositories'][self.repo_name]]
765 767 )
766 768 except KeyError:
767 769 return False
768 770 if self.required_perms.issubset(self._user_perms):
769 771 return True
770 772 return False
771 773
772 774
773 775 class HasRepoPermissionAny(PermsFunction):
774 776 def __call__(self, repo_name=None, check_Location=''):
775 777 self.repo_name = repo_name
776 778 return super(HasRepoPermissionAny, self).__call__(check_Location)
777 779
778 780 def check_permissions(self):
779 781 if not self.repo_name:
780 782 self.repo_name = get_repo_slug(request)
781 783
782 784 try:
783 785 self._user_perms = set(
784 786 [self.user_perms['repositories'][self.repo_name]]
785 787 )
786 788 except KeyError:
787 789 return False
788 790 if self.required_perms.intersection(self._user_perms):
789 791 return True
790 792 return False
791 793
792 794
793 795 class HasReposGroupPermissionAny(PermsFunction):
794 796 def __call__(self, group_name=None, check_Location=''):
795 797 self.group_name = group_name
796 798 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
797 799
798 800 def check_permissions(self):
799 801 try:
800 802 self._user_perms = set(
801 803 [self.user_perms['repositories_groups'][self.group_name]]
802 804 )
803 805 except KeyError:
804 806 return False
805 807 if self.required_perms.intersection(self._user_perms):
806 808 return True
807 809 return False
808 810
809 811
810 812 class HasReposGroupPermissionAll(PermsFunction):
811 813 def __call__(self, group_name=None, check_Location=''):
812 814 self.group_name = group_name
813 815 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
814 816
815 817 def check_permissions(self):
816 818 try:
817 819 self._user_perms = set(
818 820 [self.user_perms['repositories_groups'][self.group_name]]
819 821 )
820 822 except KeyError:
821 823 return False
822 824 if self.required_perms.issubset(self._user_perms):
823 825 return True
824 826 return False
825 827
826 828
827 829 #==============================================================================
828 830 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
829 831 #==============================================================================
830 832 class HasPermissionAnyMiddleware(object):
831 833 def __init__(self, *perms):
832 834 self.required_perms = set(perms)
833 835
834 836 def __call__(self, user, repo_name):
835 837 # repo_name MUST be unicode, since we handle keys in permission
836 838 # dict by unicode
837 839 repo_name = safe_unicode(repo_name)
838 840 usr = AuthUser(user.user_id)
839 841 try:
840 842 self.user_perms = set([usr.permissions['repositories'][repo_name]])
841 843 except Exception:
842 844 log.error('Exception while accessing permissions %s' %
843 845 traceback.format_exc())
844 846 self.user_perms = set()
845 847 self.username = user.username
846 848 self.repo_name = repo_name
847 849 return self.check_permissions()
848 850
849 851 def check_permissions(self):
850 852 log.debug('checking VCS protocol '
851 853 'permissions %s for user:%s repository:%s', self.user_perms,
852 854 self.username, self.repo_name)
853 855 if self.required_perms.intersection(self.user_perms):
854 856 log.debug('permission granted for user:%s on repo:%s' % (
855 857 self.username, self.repo_name
856 858 )
857 859 )
858 860 return True
859 861 log.debug('permission denied for user:%s on repo:%s' % (
860 862 self.username, self.repo_name
861 863 )
862 864 )
863 865 return False
864 866
865 867
866 868 #==============================================================================
867 869 # SPECIAL VERSION TO HANDLE API AUTH
868 870 #==============================================================================
869 871 class _BaseApiPerm(object):
870 872 def __init__(self, *perms):
871 873 self.required_perms = set(perms)
872 874
873 875 def __call__(self, check_location='unspecified', user=None, repo_name=None):
874 876 cls_name = self.__class__.__name__
875 877 check_scope = 'user:%s, repo:%s' % (user, repo_name)
876 878 log.debug('checking cls:%s %s %s @ %s', cls_name,
877 879 self.required_perms, check_scope, check_location)
878 880 if not user:
879 881 log.debug('Empty User passed into arguments')
880 882 return False
881 883
882 884 ## process user
883 885 if not isinstance(user, AuthUser):
884 886 user = AuthUser(user.user_id)
885 887
886 888 if self.check_permissions(user.permissions, repo_name):
887 889 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
888 890 user, check_location)
889 891 return True
890 892
891 893 else:
892 894 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
893 895 user, check_location)
894 896 return False
895 897
896 898 def check_permissions(self, perm_defs, repo_name):
897 899 """
898 900 implement in child class should return True if permissions are ok,
899 901 False otherwise
900 902
901 903 :param perm_defs: dict with permission definitions
902 904 :param repo_name: repo name
903 905 """
904 906 raise NotImplementedError()
905 907
906 908
907 909 class HasPermissionAllApi(_BaseApiPerm):
908 910 def __call__(self, user, check_location=''):
909 911 return super(HasPermissionAllApi, self)\
910 912 .__call__(check_location=check_location, user=user)
911 913
912 914 def check_permissions(self, perm_defs, repo):
913 915 if self.required_perms.issubset(perm_defs.get('global')):
914 916 return True
915 917 return False
916 918
917 919
918 920 class HasPermissionAnyApi(_BaseApiPerm):
919 921 def __call__(self, user, check_location=''):
920 922 return super(HasPermissionAnyApi, self)\
921 923 .__call__(check_location=check_location, user=user)
922 924
923 925 def check_permissions(self, perm_defs, repo):
924 926 if self.required_perms.intersection(perm_defs.get('global')):
925 927 return True
926 928 return False
927 929
928 930
929 931 class HasRepoPermissionAllApi(_BaseApiPerm):
930 932 def __call__(self, user, repo_name, check_location=''):
931 933 return super(HasRepoPermissionAllApi, self)\
932 934 .__call__(check_location=check_location, user=user,
933 935 repo_name=repo_name)
934 936
935 937 def check_permissions(self, perm_defs, repo_name):
936 938
937 939 try:
938 940 self._user_perms = set(
939 941 [perm_defs['repositories'][repo_name]]
940 942 )
941 943 except KeyError:
942 944 log.warning(traceback.format_exc())
943 945 return False
944 946 if self.required_perms.issubset(self._user_perms):
945 947 return True
946 948 return False
947 949
948 950
949 951 class HasRepoPermissionAnyApi(_BaseApiPerm):
950 952 def __call__(self, user, repo_name, check_location=''):
951 953 return super(HasRepoPermissionAnyApi, self)\
952 954 .__call__(check_location=check_location, user=user,
953 955 repo_name=repo_name)
954 956
955 957 def check_permissions(self, perm_defs, repo_name):
956 958
957 959 try:
958 960 _user_perms = set(
959 961 [perm_defs['repositories'][repo_name]]
960 962 )
961 963 except KeyError:
962 964 log.warning(traceback.format_exc())
963 965 return False
964 966 if self.required_perms.intersection(_user_perms):
965 967 return True
966 968 return False
967 969
968 970
969 971 def check_ip_access(source_ip, allowed_ips=None):
970 972 """
971 973 Checks if source_ip is a subnet of any of allowed_ips.
972 974
973 975 :param source_ip:
974 976 :param allowed_ips: list of allowed ips together with mask
975 977 """
976 978 from rhodecode.lib import ipaddr
977 979 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
978 980 if isinstance(allowed_ips, (tuple, list, set)):
979 981 for ip in allowed_ips:
980 982 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
981 983 return True
982 984 return False
General Comments 0
You need to be logged in to leave comments. Login now