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