##// END OF EJS Templates
adde cleanup username flag into get_container_username function
marcink -
r3173:db0871d9 beta
parent child Browse files
Show More
@@ -1,984 +1,995 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 def get_container_username(environ, config):
273 def get_container_username(environ, config, clean_username=False):
274 """
275 Get's the container_auth username (or email). It tries to get username
276 from REMOTE_USER if container_auth_enabled is enabled, if that fails
277 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
278 is enabled. clean_username extracts the username from this data if it's
279 having @ in it.
280
281 :param environ:
282 :param config:
283 :param clean_username:
284 """
274 285 username = None
275 286
276 287 if str2bool(config.get('container_auth_enabled', False)):
277 288 from paste.httpheaders import REMOTE_USER
278 289 username = REMOTE_USER(environ)
279 290 log.debug('extracted REMOTE_USER:%s' % (username))
280 291
281 292 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
282 293 username = environ.get('HTTP_X_FORWARDED_USER')
283 294 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
284 295
285 if username:
296 if username and clean_username:
286 297 # Removing realm and domain from username
287 298 username = username.partition('@')[0]
288 299 username = username.rpartition('\\')[2]
289 300 log.debug('Received username %s from container' % username)
290 301
291 302 return username
292 303
293 304
294 305 class CookieStoreWrapper(object):
295 306
296 307 def __init__(self, cookie_store):
297 308 self.cookie_store = cookie_store
298 309
299 310 def __repr__(self):
300 311 return 'CookieStore<%s>' % (self.cookie_store)
301 312
302 313 def get(self, key, other=None):
303 314 if isinstance(self.cookie_store, dict):
304 315 return self.cookie_store.get(key, other)
305 316 elif isinstance(self.cookie_store, AuthUser):
306 317 return self.cookie_store.__dict__.get(key, other)
307 318
308 319
309 320 class AuthUser(object):
310 321 """
311 322 A simple object that handles all attributes of user in RhodeCode
312 323
313 324 It does lookup based on API key,given user, or user present in session
314 325 Then it fills all required information for such user. It also checks if
315 326 anonymous access is enabled and if so, it returns default user as logged
316 327 in
317 328 """
318 329
319 330 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
320 331
321 332 self.user_id = user_id
322 333 self.api_key = None
323 334 self.username = username
324 335 self.ip_addr = ip_addr
325 336
326 337 self.name = ''
327 338 self.lastname = ''
328 339 self.email = ''
329 340 self.is_authenticated = False
330 341 self.admin = False
331 342 self.inherit_default_permissions = False
332 343 self.permissions = {}
333 344 self._api_key = api_key
334 345 self.propagate_data()
335 346 self._instance = None
336 347
337 348 def propagate_data(self):
338 349 user_model = UserModel()
339 350 self.anonymous_user = User.get_by_username('default', cache=True)
340 351 is_user_loaded = False
341 352
342 353 # try go get user by api key
343 354 if self._api_key and self._api_key != self.anonymous_user.api_key:
344 355 log.debug('Auth User lookup by API KEY %s' % self._api_key)
345 356 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
346 357 # lookup by userid
347 358 elif (self.user_id is not None and
348 359 self.user_id != self.anonymous_user.user_id):
349 360 log.debug('Auth User lookup by USER ID %s' % self.user_id)
350 361 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
351 362 # lookup by username
352 363 elif self.username and \
353 364 str2bool(config.get('container_auth_enabled', False)):
354 365
355 366 log.debug('Auth User lookup by USER NAME %s' % self.username)
356 367 dbuser = login_container_auth(self.username)
357 368 if dbuser is not None:
358 369 log.debug('filling all attributes to object')
359 370 for k, v in dbuser.get_dict().items():
360 371 setattr(self, k, v)
361 372 self.set_authenticated()
362 373 is_user_loaded = True
363 374 else:
364 375 log.debug('No data in %s that could been used to log in' % self)
365 376
366 377 if not is_user_loaded:
367 378 # if we cannot authenticate user try anonymous
368 379 if self.anonymous_user.active is True:
369 380 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
370 381 # then we set this user is logged in
371 382 self.is_authenticated = True
372 383 else:
373 384 self.user_id = None
374 385 self.username = None
375 386 self.is_authenticated = False
376 387
377 388 if not self.username:
378 389 self.username = 'None'
379 390
380 391 log.debug('Auth User is now %s' % self)
381 392 user_model.fill_perms(self)
382 393
383 394 @property
384 395 def is_admin(self):
385 396 return self.admin
386 397
387 398 @property
388 399 def ip_allowed(self):
389 400 """
390 401 Checks if ip_addr used in constructor is allowed from defined list of
391 402 allowed ip_addresses for user
392 403
393 404 :returns: boolean, True if ip is in allowed ip range
394 405 """
395 406 #check IP
396 407 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
397 408 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
398 409 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
399 410 return True
400 411 else:
401 412 log.info('Access for IP:%s forbidden, '
402 413 'not in %s' % (self.ip_addr, allowed_ips))
403 414 return False
404 415
405 416 def __repr__(self):
406 417 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
407 418 self.is_authenticated)
408 419
409 420 def set_authenticated(self, authenticated=True):
410 421 if self.user_id != self.anonymous_user.user_id:
411 422 self.is_authenticated = authenticated
412 423
413 424 def get_cookie_store(self):
414 425 return {'username': self.username,
415 426 'user_id': self.user_id,
416 427 'is_authenticated': self.is_authenticated}
417 428
418 429 @classmethod
419 430 def from_cookie_store(cls, cookie_store):
420 431 """
421 432 Creates AuthUser from a cookie store
422 433
423 434 :param cls:
424 435 :param cookie_store:
425 436 """
426 437 user_id = cookie_store.get('user_id')
427 438 username = cookie_store.get('username')
428 439 api_key = cookie_store.get('api_key')
429 440 return AuthUser(user_id, api_key, username)
430 441
431 442 @classmethod
432 443 def get_allowed_ips(cls, user_id, cache=False):
433 444 _set = set()
434 445 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
435 446 if cache:
436 447 user_ips = user_ips.options(FromCache("sql_cache_short",
437 448 "get_user_ips_%s" % user_id))
438 449 for ip in user_ips:
439 450 _set.add(ip.ip_addr)
440 451 return _set or set(['0.0.0.0/0'])
441 452
442 453
443 454 def set_available_permissions(config):
444 455 """
445 456 This function will propagate pylons globals with all available defined
446 457 permission given in db. We don't want to check each time from db for new
447 458 permissions since adding a new permission also requires application restart
448 459 ie. to decorate new views with the newly created permission
449 460
450 461 :param config: current pylons config instance
451 462
452 463 """
453 464 log.info('getting information about all available permissions')
454 465 try:
455 466 sa = meta.Session
456 467 all_perms = sa.query(Permission).all()
457 468 except Exception:
458 469 pass
459 470 finally:
460 471 meta.Session.remove()
461 472
462 473 config['available_permissions'] = [x.permission_name for x in all_perms]
463 474
464 475
465 476 #==============================================================================
466 477 # CHECK DECORATORS
467 478 #==============================================================================
468 479 class LoginRequired(object):
469 480 """
470 481 Must be logged in to execute this function else
471 482 redirect to login page
472 483
473 484 :param api_access: if enabled this checks only for valid auth token
474 485 and grants access based on valid token
475 486 """
476 487
477 488 def __init__(self, api_access=False):
478 489 self.api_access = api_access
479 490
480 491 def __call__(self, func):
481 492 return decorator(self.__wrapper, func)
482 493
483 494 def __wrapper(self, func, *fargs, **fkwargs):
484 495 cls = fargs[0]
485 496 user = cls.rhodecode_user
486 497 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
487 498
488 499 #check IP
489 500 ip_access_ok = True
490 501 if not user.ip_allowed:
491 502 from rhodecode.lib import helpers as h
492 503 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
493 504 category='warning')
494 505 ip_access_ok = False
495 506
496 507 api_access_ok = False
497 508 if self.api_access:
498 509 log.debug('Checking API KEY access for %s' % cls)
499 510 if user.api_key == request.GET.get('api_key'):
500 511 api_access_ok = True
501 512 else:
502 513 log.debug("API KEY token not valid")
503 514
504 515 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
505 516 if (user.is_authenticated or api_access_ok) and ip_access_ok:
506 517 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
507 518 log.info('user %s is authenticated and granted access to %s '
508 519 'using %s' % (user.username, loc, reason)
509 520 )
510 521 return func(*fargs, **fkwargs)
511 522 else:
512 523 log.warn('user %s NOT authenticated on func: %s' % (
513 524 user, loc)
514 525 )
515 526 p = url.current()
516 527
517 528 log.debug('redirecting to login page with %s' % p)
518 529 return redirect(url('login_home', came_from=p))
519 530
520 531
521 532 class NotAnonymous(object):
522 533 """
523 534 Must be logged in to execute this function else
524 535 redirect to login page"""
525 536
526 537 def __call__(self, func):
527 538 return decorator(self.__wrapper, func)
528 539
529 540 def __wrapper(self, func, *fargs, **fkwargs):
530 541 cls = fargs[0]
531 542 self.user = cls.rhodecode_user
532 543
533 544 log.debug('Checking if user is not anonymous @%s' % cls)
534 545
535 546 anonymous = self.user.username == 'default'
536 547
537 548 if anonymous:
538 549 p = url.current()
539 550
540 551 import rhodecode.lib.helpers as h
541 552 h.flash(_('You need to be a registered user to '
542 553 'perform this action'),
543 554 category='warning')
544 555 return redirect(url('login_home', came_from=p))
545 556 else:
546 557 return func(*fargs, **fkwargs)
547 558
548 559
549 560 class PermsDecorator(object):
550 561 """Base class for controller decorators"""
551 562
552 563 def __init__(self, *required_perms):
553 564 available_perms = config['available_permissions']
554 565 for perm in required_perms:
555 566 if perm not in available_perms:
556 567 raise Exception("'%s' permission is not defined" % perm)
557 568 self.required_perms = set(required_perms)
558 569 self.user_perms = None
559 570
560 571 def __call__(self, func):
561 572 return decorator(self.__wrapper, func)
562 573
563 574 def __wrapper(self, func, *fargs, **fkwargs):
564 575 cls = fargs[0]
565 576 self.user = cls.rhodecode_user
566 577 self.user_perms = self.user.permissions
567 578 log.debug('checking %s permissions %s for %s %s',
568 579 self.__class__.__name__, self.required_perms, cls, self.user)
569 580
570 581 if self.check_permissions():
571 582 log.debug('Permission granted for %s %s' % (cls, self.user))
572 583 return func(*fargs, **fkwargs)
573 584
574 585 else:
575 586 log.debug('Permission denied for %s %s' % (cls, self.user))
576 587 anonymous = self.user.username == 'default'
577 588
578 589 if anonymous:
579 590 p = url.current()
580 591
581 592 import rhodecode.lib.helpers as h
582 593 h.flash(_('You need to be a signed in to '
583 594 'view this page'),
584 595 category='warning')
585 596 return redirect(url('login_home', came_from=p))
586 597
587 598 else:
588 599 # redirect with forbidden ret code
589 600 return abort(403)
590 601
591 602 def check_permissions(self):
592 603 """Dummy function for overriding"""
593 604 raise Exception('You have to write this function in child class')
594 605
595 606
596 607 class HasPermissionAllDecorator(PermsDecorator):
597 608 """
598 609 Checks for access permission for all given predicates. All of them
599 610 have to be meet in order to fulfill the request
600 611 """
601 612
602 613 def check_permissions(self):
603 614 if self.required_perms.issubset(self.user_perms.get('global')):
604 615 return True
605 616 return False
606 617
607 618
608 619 class HasPermissionAnyDecorator(PermsDecorator):
609 620 """
610 621 Checks for access permission for any of given predicates. In order to
611 622 fulfill the request any of predicates must be meet
612 623 """
613 624
614 625 def check_permissions(self):
615 626 if self.required_perms.intersection(self.user_perms.get('global')):
616 627 return True
617 628 return False
618 629
619 630
620 631 class HasRepoPermissionAllDecorator(PermsDecorator):
621 632 """
622 633 Checks for access permission for all given predicates for specific
623 634 repository. All of them have to be meet in order to fulfill the request
624 635 """
625 636
626 637 def check_permissions(self):
627 638 repo_name = get_repo_slug(request)
628 639 try:
629 640 user_perms = set([self.user_perms['repositories'][repo_name]])
630 641 except KeyError:
631 642 return False
632 643 if self.required_perms.issubset(user_perms):
633 644 return True
634 645 return False
635 646
636 647
637 648 class HasRepoPermissionAnyDecorator(PermsDecorator):
638 649 """
639 650 Checks for access permission for any of given predicates for specific
640 651 repository. In order to fulfill the request any of predicates must be meet
641 652 """
642 653
643 654 def check_permissions(self):
644 655 repo_name = get_repo_slug(request)
645 656
646 657 try:
647 658 user_perms = set([self.user_perms['repositories'][repo_name]])
648 659 except KeyError:
649 660 return False
650 661
651 662 if self.required_perms.intersection(user_perms):
652 663 return True
653 664 return False
654 665
655 666
656 667 class HasReposGroupPermissionAllDecorator(PermsDecorator):
657 668 """
658 669 Checks for access permission for all given predicates for specific
659 670 repository. All of them have to be meet in order to fulfill the request
660 671 """
661 672
662 673 def check_permissions(self):
663 674 group_name = get_repos_group_slug(request)
664 675 try:
665 676 user_perms = set([self.user_perms['repositories_groups'][group_name]])
666 677 except KeyError:
667 678 return False
668 679 if self.required_perms.issubset(user_perms):
669 680 return True
670 681 return False
671 682
672 683
673 684 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
674 685 """
675 686 Checks for access permission for any of given predicates for specific
676 687 repository. In order to fulfill the request any of predicates must be meet
677 688 """
678 689
679 690 def check_permissions(self):
680 691 group_name = get_repos_group_slug(request)
681 692
682 693 try:
683 694 user_perms = set([self.user_perms['repositories_groups'][group_name]])
684 695 except KeyError:
685 696 return False
686 697 if self.required_perms.intersection(user_perms):
687 698 return True
688 699 return False
689 700
690 701
691 702 #==============================================================================
692 703 # CHECK FUNCTIONS
693 704 #==============================================================================
694 705 class PermsFunction(object):
695 706 """Base function for other check functions"""
696 707
697 708 def __init__(self, *perms):
698 709 available_perms = config['available_permissions']
699 710
700 711 for perm in perms:
701 712 if perm not in available_perms:
702 713 raise Exception("'%s' permission is not defined" % perm)
703 714 self.required_perms = set(perms)
704 715 self.user_perms = None
705 716 self.repo_name = None
706 717 self.group_name = None
707 718
708 719 def __call__(self, check_Location=''):
709 720 user = request.user
710 721 cls_name = self.__class__.__name__
711 722 check_scope = {
712 723 'HasPermissionAll': '',
713 724 'HasPermissionAny': '',
714 725 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
715 726 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
716 727 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
717 728 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
718 729 }.get(cls_name, '?')
719 730 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
720 731 self.required_perms, user, check_scope,
721 732 check_Location or 'unspecified location')
722 733 if not user:
723 734 log.debug('Empty request user')
724 735 return False
725 736 self.user_perms = user.permissions
726 737 if self.check_permissions():
727 738 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
728 739 check_Location or 'unspecified location')
729 740 return True
730 741
731 742 else:
732 743 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
733 744 check_Location or 'unspecified location')
734 745 return False
735 746
736 747 def check_permissions(self):
737 748 """Dummy function for overriding"""
738 749 raise Exception('You have to write this function in child class')
739 750
740 751
741 752 class HasPermissionAll(PermsFunction):
742 753 def check_permissions(self):
743 754 if self.required_perms.issubset(self.user_perms.get('global')):
744 755 return True
745 756 return False
746 757
747 758
748 759 class HasPermissionAny(PermsFunction):
749 760 def check_permissions(self):
750 761 if self.required_perms.intersection(self.user_perms.get('global')):
751 762 return True
752 763 return False
753 764
754 765
755 766 class HasRepoPermissionAll(PermsFunction):
756 767 def __call__(self, repo_name=None, check_Location=''):
757 768 self.repo_name = repo_name
758 769 return super(HasRepoPermissionAll, self).__call__(check_Location)
759 770
760 771 def check_permissions(self):
761 772 if not self.repo_name:
762 773 self.repo_name = get_repo_slug(request)
763 774
764 775 try:
765 776 self._user_perms = set(
766 777 [self.user_perms['repositories'][self.repo_name]]
767 778 )
768 779 except KeyError:
769 780 return False
770 781 if self.required_perms.issubset(self._user_perms):
771 782 return True
772 783 return False
773 784
774 785
775 786 class HasRepoPermissionAny(PermsFunction):
776 787 def __call__(self, repo_name=None, check_Location=''):
777 788 self.repo_name = repo_name
778 789 return super(HasRepoPermissionAny, self).__call__(check_Location)
779 790
780 791 def check_permissions(self):
781 792 if not self.repo_name:
782 793 self.repo_name = get_repo_slug(request)
783 794
784 795 try:
785 796 self._user_perms = set(
786 797 [self.user_perms['repositories'][self.repo_name]]
787 798 )
788 799 except KeyError:
789 800 return False
790 801 if self.required_perms.intersection(self._user_perms):
791 802 return True
792 803 return False
793 804
794 805
795 806 class HasReposGroupPermissionAny(PermsFunction):
796 807 def __call__(self, group_name=None, check_Location=''):
797 808 self.group_name = group_name
798 809 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
799 810
800 811 def check_permissions(self):
801 812 try:
802 813 self._user_perms = set(
803 814 [self.user_perms['repositories_groups'][self.group_name]]
804 815 )
805 816 except KeyError:
806 817 return False
807 818 if self.required_perms.intersection(self._user_perms):
808 819 return True
809 820 return False
810 821
811 822
812 823 class HasReposGroupPermissionAll(PermsFunction):
813 824 def __call__(self, group_name=None, check_Location=''):
814 825 self.group_name = group_name
815 826 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
816 827
817 828 def check_permissions(self):
818 829 try:
819 830 self._user_perms = set(
820 831 [self.user_perms['repositories_groups'][self.group_name]]
821 832 )
822 833 except KeyError:
823 834 return False
824 835 if self.required_perms.issubset(self._user_perms):
825 836 return True
826 837 return False
827 838
828 839
829 840 #==============================================================================
830 841 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
831 842 #==============================================================================
832 843 class HasPermissionAnyMiddleware(object):
833 844 def __init__(self, *perms):
834 845 self.required_perms = set(perms)
835 846
836 847 def __call__(self, user, repo_name):
837 848 # repo_name MUST be unicode, since we handle keys in permission
838 849 # dict by unicode
839 850 repo_name = safe_unicode(repo_name)
840 851 usr = AuthUser(user.user_id)
841 852 try:
842 853 self.user_perms = set([usr.permissions['repositories'][repo_name]])
843 854 except Exception:
844 855 log.error('Exception while accessing permissions %s' %
845 856 traceback.format_exc())
846 857 self.user_perms = set()
847 858 self.username = user.username
848 859 self.repo_name = repo_name
849 860 return self.check_permissions()
850 861
851 862 def check_permissions(self):
852 863 log.debug('checking VCS protocol '
853 864 'permissions %s for user:%s repository:%s', self.user_perms,
854 865 self.username, self.repo_name)
855 866 if self.required_perms.intersection(self.user_perms):
856 867 log.debug('permission granted for user:%s on repo:%s' % (
857 868 self.username, self.repo_name
858 869 )
859 870 )
860 871 return True
861 872 log.debug('permission denied for user:%s on repo:%s' % (
862 873 self.username, self.repo_name
863 874 )
864 875 )
865 876 return False
866 877
867 878
868 879 #==============================================================================
869 880 # SPECIAL VERSION TO HANDLE API AUTH
870 881 #==============================================================================
871 882 class _BaseApiPerm(object):
872 883 def __init__(self, *perms):
873 884 self.required_perms = set(perms)
874 885
875 886 def __call__(self, check_location='unspecified', user=None, repo_name=None):
876 887 cls_name = self.__class__.__name__
877 888 check_scope = 'user:%s, repo:%s' % (user, repo_name)
878 889 log.debug('checking cls:%s %s %s @ %s', cls_name,
879 890 self.required_perms, check_scope, check_location)
880 891 if not user:
881 892 log.debug('Empty User passed into arguments')
882 893 return False
883 894
884 895 ## process user
885 896 if not isinstance(user, AuthUser):
886 897 user = AuthUser(user.user_id)
887 898
888 899 if self.check_permissions(user.permissions, repo_name):
889 900 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
890 901 user, check_location)
891 902 return True
892 903
893 904 else:
894 905 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
895 906 user, check_location)
896 907 return False
897 908
898 909 def check_permissions(self, perm_defs, repo_name):
899 910 """
900 911 implement in child class should return True if permissions are ok,
901 912 False otherwise
902 913
903 914 :param perm_defs: dict with permission definitions
904 915 :param repo_name: repo name
905 916 """
906 917 raise NotImplementedError()
907 918
908 919
909 920 class HasPermissionAllApi(_BaseApiPerm):
910 921 def __call__(self, user, check_location=''):
911 922 return super(HasPermissionAllApi, self)\
912 923 .__call__(check_location=check_location, user=user)
913 924
914 925 def check_permissions(self, perm_defs, repo):
915 926 if self.required_perms.issubset(perm_defs.get('global')):
916 927 return True
917 928 return False
918 929
919 930
920 931 class HasPermissionAnyApi(_BaseApiPerm):
921 932 def __call__(self, user, check_location=''):
922 933 return super(HasPermissionAnyApi, self)\
923 934 .__call__(check_location=check_location, user=user)
924 935
925 936 def check_permissions(self, perm_defs, repo):
926 937 if self.required_perms.intersection(perm_defs.get('global')):
927 938 return True
928 939 return False
929 940
930 941
931 942 class HasRepoPermissionAllApi(_BaseApiPerm):
932 943 def __call__(self, user, repo_name, check_location=''):
933 944 return super(HasRepoPermissionAllApi, self)\
934 945 .__call__(check_location=check_location, user=user,
935 946 repo_name=repo_name)
936 947
937 948 def check_permissions(self, perm_defs, repo_name):
938 949
939 950 try:
940 951 self._user_perms = set(
941 952 [perm_defs['repositories'][repo_name]]
942 953 )
943 954 except KeyError:
944 955 log.warning(traceback.format_exc())
945 956 return False
946 957 if self.required_perms.issubset(self._user_perms):
947 958 return True
948 959 return False
949 960
950 961
951 962 class HasRepoPermissionAnyApi(_BaseApiPerm):
952 963 def __call__(self, user, repo_name, check_location=''):
953 964 return super(HasRepoPermissionAnyApi, self)\
954 965 .__call__(check_location=check_location, user=user,
955 966 repo_name=repo_name)
956 967
957 968 def check_permissions(self, perm_defs, repo_name):
958 969
959 970 try:
960 971 _user_perms = set(
961 972 [perm_defs['repositories'][repo_name]]
962 973 )
963 974 except KeyError:
964 975 log.warning(traceback.format_exc())
965 976 return False
966 977 if self.required_perms.intersection(_user_perms):
967 978 return True
968 979 return False
969 980
970 981
971 982 def check_ip_access(source_ip, allowed_ips=None):
972 983 """
973 984 Checks if source_ip is a subnet of any of allowed_ips.
974 985
975 986 :param source_ip:
976 987 :param allowed_ips: list of allowed ips together with mask
977 988 """
978 989 from rhodecode.lib import ipaddr
979 990 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
980 991 if isinstance(allowed_ips, (tuple, list, set)):
981 992 for ip in allowed_ips:
982 993 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
983 994 return True
984 995 return False
General Comments 0
You need to be logged in to leave comments. Login now