comparison rhodecode/lib/auth.py @ 4116:ffd45b185016 rhodecode-2.2.5-gpl

Imported some of the GPLv3'd changes from RhodeCode v2.2.5. This imports changes between changesets 21af6c4eab3d and 6177597791c2 in RhodeCode's original repository, including only changes to Python files and HTML. RhodeCode clearly licensed its changes to these files under GPLv3 in their /LICENSE file, which states the following: The Python code and integrated HTML are licensed under the GPLv3 license. (See: https://code.rhodecode.com/rhodecode/files/v2.2.5/LICENSE or http://web.archive.org/web/20140512193334/https://code.rhodecode.com/rhodecode/files/f3b123159901f15426d18e3dc395e8369f70ebe0/LICENSE for an online copy of that LICENSE file) Conservancy reviewed these changes and confirmed that they can be licensed as a whole to the Kallithea project under GPLv3-only. While some of the contents committed herein are clearly licensed GPLv3-or-later, on the whole we must assume the are GPLv3-only, since the statement above from RhodeCode indicates that they intend GPLv3-only as their license, per GPLv3ยง14 and other relevant sections of GPLv3.
author Bradley M. Kuhn <bkuhn@sfconservancy.org>
date Wed, 02 Jul 2014 19:03:13 -0400
parents a5888ca796b5
children 7e5f8c12a3fc
comparison
equal deleted inserted replaced
4115:8b7294a804a0 4116:ffd45b185016
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
5
6 authentication and permission libraries
7
8 :created_on: Apr 4, 2010
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
13 # This program is free software: you can redistribute it and/or modify 2 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by 3 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or 4 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version. 5 # (at your option) any later version.
17 # 6 #
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details. 10 # GNU General Public License for more details.
22 # 11 #
23 # You should have received a copy of the GNU General Public License 12 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>. 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 14 """
15 rhodecode.lib.auth
16 ~~~~~~~~~~~~~~~~~~
17
18 authentication and permission libraries
19
20 :created_on: Apr 4, 2010
21 :author: marcink
22 :copyright: (c) 2013 RhodeCode GmbH.
23 :license: GPLv3, see LICENSE for more details.
24 """
25 from __future__ import with_statement
26 import time
26 import random 27 import random
27 import logging 28 import logging
28 import traceback 29 import traceback
29 import hashlib 30 import hashlib
31 import itertools
32 import collections
30 33
31 from tempfile import _RandomNameSequence 34 from tempfile import _RandomNameSequence
32 from decorator import decorator 35 from decorator import decorator
33 36
34 from pylons import config, url, request 37 from pylons import url, request
35 from pylons.controllers.util import abort, redirect 38 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _ 39 from pylons.i18n.translation import _
40 from sqlalchemy import or_
37 from sqlalchemy.orm.exc import ObjectDeletedError 41 from sqlalchemy.orm.exc import ObjectDeletedError
42 from sqlalchemy.orm import joinedload
38 43
39 from rhodecode import __platform__, is_windows, is_unix 44 from rhodecode import __platform__, is_windows, is_unix
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.model import meta
40 from rhodecode.model.meta import Session 47 from rhodecode.model.meta import Session
41
42 from rhodecode.lib.utils2 import str2bool, safe_unicode, aslist
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError,\
44 LdapImportError
45 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug,\
46 get_user_group_slug
47 from rhodecode.lib.auth_ldap import AuthLdap
48
49 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel 48 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap 49 from rhodecode.model.db import User, Repository, Permission, \
50 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
51 RepoGroup, UserGroupRepoGroupToPerm, UserIpMap, UserGroupUserGroupToPerm, \
52 UserGroup, UserApiKeys
53
54 from rhodecode.lib.utils2 import safe_unicode, aslist
55 from rhodecode.lib.utils import get_repo_slug, get_repo_group_slug, \
56 get_user_group_slug, conditional_cache
52 from rhodecode.lib.caching_query import FromCache 57 from rhodecode.lib.caching_query import FromCache
58
59 from beaker.cache import cache_region
53 60
54 log = logging.getLogger(__name__) 61 log = logging.getLogger(__name__)
55 62
56 63
57 class PasswordGenerator(object): 64 class PasswordGenerator(object):
147 salt = _RandomNameSequence().next() 154 salt = _RandomNameSequence().next()
148 155
149 return hashlib.sha1(str_ + salt).hexdigest() 156 return hashlib.sha1(str_ + salt).hexdigest()
150 157
151 158
152 def authfunc(environ, username, password):
153 """
154 Dummy authentication wrapper function used in Mercurial and Git for
155 access control.
156
157 :param environ: needed only for using in Basic auth
158 """
159 return authenticate(username, password)
160
161
162 def authenticate(username, password):
163 """
164 Authentication function used for access control,
165 firstly checks for db authentication then if ldap is enabled for ldap
166 authentication, also creates ldap user if not in database
167
168 :param username: username
169 :param password: password
170 """
171
172 user_model = UserModel()
173 user = User.get_by_username(username)
174
175 log.debug('Authenticating user using RhodeCode account')
176 if user is not None and not user.ldap_dn:
177 if user.active:
178 if user.username == 'default' and user.active:
179 log.info('user %s authenticated correctly as anonymous user' %
180 username)
181 return True
182
183 elif user.username == username and check_password(password,
184 user.password):
185 log.info('user %s authenticated correctly' % username)
186 return True
187 else:
188 log.warning('user %s tried auth but is disabled' % username)
189
190 else:
191 log.debug('Regular authentication failed')
192 user_obj = User.get_by_username(username, case_insensitive=True)
193
194 if user_obj is not None and not user_obj.ldap_dn:
195 log.debug('this user already exists as non ldap')
196 return False
197
198 ldap_settings = RhodeCodeSetting.get_ldap_settings()
199 #======================================================================
200 # FALLBACK TO LDAP AUTH IF ENABLE
201 #======================================================================
202 if str2bool(ldap_settings.get('ldap_active')):
203 log.debug("Authenticating user using ldap")
204 kwargs = {
205 'server': ldap_settings.get('ldap_host', ''),
206 'base_dn': ldap_settings.get('ldap_base_dn', ''),
207 'port': ldap_settings.get('ldap_port'),
208 'bind_dn': ldap_settings.get('ldap_dn_user'),
209 'bind_pass': ldap_settings.get('ldap_dn_pass'),
210 'tls_kind': ldap_settings.get('ldap_tls_kind'),
211 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
212 'ldap_filter': ldap_settings.get('ldap_filter'),
213 'search_scope': ldap_settings.get('ldap_search_scope'),
214 'attr_login': ldap_settings.get('ldap_attr_login'),
215 'ldap_version': 3,
216 }
217 log.debug('Checking for ldap authentication')
218 try:
219 aldap = AuthLdap(**kwargs)
220 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
221 password)
222 log.debug('Got ldap DN response %s' % user_dn)
223
224 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
225 .get(k), [''])[0]
226
227 user_attrs = {
228 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
229 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
230 'email': get_ldap_attr('ldap_attr_email'),
231 'active': 'hg.extern_activate.auto' in User.get_default_user()\
232 .AuthUser.permissions['global']
233 }
234
235 # don't store LDAP password since we don't need it. Override
236 # with some random generated password
237 _password = PasswordGenerator().gen_password(length=8)
238 # create this user on the fly if it doesn't exist in rhodecode
239 # database
240 if user_model.create_ldap(username, _password, user_dn,
241 user_attrs):
242 log.info('created new ldap user %s' % username)
243
244 Session().commit()
245 return True
246 except (LdapUsernameError, LdapPasswordError, LdapImportError):
247 pass
248 except (Exception,):
249 log.error(traceback.format_exc())
250 pass
251 return False
252
253
254 def login_container_auth(username):
255 user = User.get_by_username(username)
256 if user is None:
257 user_attrs = {
258 'name': username,
259 'lastname': None,
260 'email': None,
261 'active': 'hg.extern_activate.auto' in User.get_default_user()\
262 .AuthUser.permissions['global']
263 }
264 user = UserModel().create_for_container_auth(username, user_attrs)
265 if not user:
266 return None
267 log.info('User %s was created by container authentication' % username)
268
269 if not user.active:
270 return None
271
272 user.update_lastlogin()
273 Session().commit()
274
275 log.debug('User %s is now logged in by container authentication',
276 user.username)
277 return user
278
279
280 def get_container_username(environ, config, clean_username=False):
281 """
282 Gets the container_auth username (or email). It tries to get username
283 from REMOTE_USER if container_auth_enabled is enabled, if that fails
284 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
285 is enabled. clean_username extracts the username from this data if it's
286 having @ in it.
287
288 :param environ:
289 :param config:
290 :param clean_username:
291 """
292 username = None
293
294 if str2bool(config.get('container_auth_enabled', False)):
295 from paste.httpheaders import REMOTE_USER
296 username = REMOTE_USER(environ)
297 log.debug('extracted REMOTE_USER:%s' % (username))
298
299 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
300 username = environ.get('HTTP_X_FORWARDED_USER')
301 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
302
303 if username and clean_username:
304 # Removing realm and domain from username
305 username = username.partition('@')[0]
306 username = username.rpartition('\\')[2]
307 log.debug('Received username %s from container' % username)
308
309 return username
310
311
312 class CookieStoreWrapper(object): 159 class CookieStoreWrapper(object):
313 160
314 def __init__(self, cookie_store): 161 def __init__(self, cookie_store):
315 self.cookie_store = cookie_store 162 self.cookie_store = cookie_store
316 163
322 return self.cookie_store.get(key, other) 169 return self.cookie_store.get(key, other)
323 elif isinstance(self.cookie_store, AuthUser): 170 elif isinstance(self.cookie_store, AuthUser):
324 return self.cookie_store.__dict__.get(key, other) 171 return self.cookie_store.__dict__.get(key, other)
325 172
326 173
327 class AuthUser(object): 174
175 def _cached_perms_data(user_id, user_is_admin, user_inherit_default_permissions,
176 explicit, algo):
177 RK = 'repositories'
178 GK = 'repositories_groups'
179 UK = 'user_groups'
180 GLOBAL = 'global'
181 PERM_WEIGHTS = Permission.PERM_WEIGHTS
182 permissions = {RK: {}, GK: {}, UK: {}, GLOBAL: set()}
183
184 def _choose_perm(new_perm, cur_perm):
185 new_perm_val = PERM_WEIGHTS[new_perm]
186 cur_perm_val = PERM_WEIGHTS[cur_perm]
187 if algo == 'higherwin':
188 if new_perm_val > cur_perm_val:
189 return new_perm
190 return cur_perm
191 elif algo == 'lowerwin':
192 if new_perm_val < cur_perm_val:
193 return new_perm
194 return cur_perm
195
196 #======================================================================
197 # fetch default permissions
198 #======================================================================
199 default_user = User.get_by_username('default', cache=True)
200 default_user_id = default_user.user_id
201
202 default_repo_perms = Permission.get_default_perms(default_user_id)
203 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
204 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
205
206 if user_is_admin:
207 #==================================================================
208 # admin user have all default rights for repositories
209 # and groups set to admin
210 #==================================================================
211 permissions[GLOBAL].add('hg.admin')
212 permissions[GLOBAL].add('hg.create.write_on_repogroup.true')
213
214 # repositories
215 for perm in default_repo_perms:
216 r_k = perm.UserRepoToPerm.repository.repo_name
217 p = 'repository.admin'
218 permissions[RK][r_k] = p
219
220 # repository groups
221 for perm in default_repo_groups_perms:
222 rg_k = perm.UserRepoGroupToPerm.group.group_name
223 p = 'group.admin'
224 permissions[GK][rg_k] = p
225
226 # user groups
227 for perm in default_user_group_perms:
228 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
229 p = 'usergroup.admin'
230 permissions[UK][u_k] = p
231 return permissions
232
233 #==================================================================
234 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
235 #==================================================================
236 uid = user_id
237
238 # default global permissions taken fron the default user
239 default_global_perms = UserToPerm.query()\
240 .filter(UserToPerm.user_id == default_user_id)\
241 .options(joinedload(UserToPerm.permission))
242
243 for perm in default_global_perms:
244 permissions[GLOBAL].add(perm.permission.permission_name)
245
246 # defaults for repositories, taken from default user
247 for perm in default_repo_perms:
248 r_k = perm.UserRepoToPerm.repository.repo_name
249 if perm.Repository.private and not (perm.Repository.user_id == uid):
250 # disable defaults for private repos,
251 p = 'repository.none'
252 elif perm.Repository.user_id == uid:
253 # set admin if owner
254 p = 'repository.admin'
255 else:
256 p = perm.Permission.permission_name
257
258 permissions[RK][r_k] = p
259
260 # defaults for repository groups taken from default user permission
261 # on given group
262 for perm in default_repo_groups_perms:
263 rg_k = perm.UserRepoGroupToPerm.group.group_name
264 p = perm.Permission.permission_name
265 permissions[GK][rg_k] = p
266
267 # defaults for user groups taken from default user permission
268 # on given user group
269 for perm in default_user_group_perms:
270 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
271 p = perm.Permission.permission_name
272 permissions[UK][u_k] = p
273
274 #======================================================================
275 # !! OVERRIDE GLOBALS !! with user permissions if any found
276 #======================================================================
277 # those can be configured from groups or users explicitly
278 _configurable = set([
279 'hg.fork.none', 'hg.fork.repository',
280 'hg.create.none', 'hg.create.repository',
281 'hg.usergroup.create.false', 'hg.usergroup.create.true'
282 ])
283
284 # USER GROUPS comes first
285 # user group global permissions
286 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
287 .options(joinedload(UserGroupToPerm.permission))\
288 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
289 UserGroupMember.users_group_id))\
290 .filter(UserGroupMember.user_id == uid)\
291 .order_by(UserGroupToPerm.users_group_id)\
292 .all()
293 # need to group here by groups since user can be in more than
294 # one group
295 _grouped = [[x, list(y)] for x, y in
296 itertools.groupby(user_perms_from_users_groups,
297 lambda x:x.users_group)]
298 for gr, perms in _grouped:
299 # since user can be in multiple groups iterate over them and
300 # select the lowest permissions first (more explicit)
301 ##TODO: do this^^
302 if not gr.inherit_default_permissions:
303 # NEED TO IGNORE all configurable permissions and
304 # replace them with explicitly set
305 permissions[GLOBAL] = permissions[GLOBAL]\
306 .difference(_configurable)
307 for perm in perms:
308 permissions[GLOBAL].add(perm.permission.permission_name)
309
310 # user specific global permissions
311 user_perms = Session().query(UserToPerm)\
312 .options(joinedload(UserToPerm.permission))\
313 .filter(UserToPerm.user_id == uid).all()
314
315 if not user_inherit_default_permissions:
316 # NEED TO IGNORE all configurable permissions and
317 # replace them with explicitly set
318 permissions[GLOBAL] = permissions[GLOBAL]\
319 .difference(_configurable)
320
321 for perm in user_perms:
322 permissions[GLOBAL].add(perm.permission.permission_name)
323 ## END GLOBAL PERMISSIONS
324
325 #======================================================================
326 # !! PERMISSIONS FOR REPOSITORIES !!
327 #======================================================================
328 #======================================================================
329 # check if user is part of user groups for this repository and
330 # fill in his permission from it. _choose_perm decides of which
331 # permission should be selected based on selected method
332 #======================================================================
333
334 # user group for repositories permissions
335 user_repo_perms_from_users_groups = \
336 Session().query(UserGroupRepoToPerm, Permission, Repository,)\
337 .join((Repository, UserGroupRepoToPerm.repository_id ==
338 Repository.repo_id))\
339 .join((Permission, UserGroupRepoToPerm.permission_id ==
340 Permission.permission_id))\
341 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
342 UserGroupMember.users_group_id))\
343 .filter(UserGroupMember.user_id == uid)\
344 .all()
345
346 multiple_counter = collections.defaultdict(int)
347 for perm in user_repo_perms_from_users_groups:
348 r_k = perm.UserGroupRepoToPerm.repository.repo_name
349 multiple_counter[r_k] += 1
350 p = perm.Permission.permission_name
351 cur_perm = permissions[RK][r_k]
352
353 if perm.Repository.user_id == uid:
354 # set admin if owner
355 p = 'repository.admin'
356 else:
357 if multiple_counter[r_k] > 1:
358 p = _choose_perm(p, cur_perm)
359 permissions[RK][r_k] = p
360
361 # user explicit permissions for repositories, overrides any specified
362 # by the group permission
363 user_repo_perms = Permission.get_default_perms(uid)
364 for perm in user_repo_perms:
365 r_k = perm.UserRepoToPerm.repository.repo_name
366 cur_perm = permissions[RK][r_k]
367 # set admin if owner
368 if perm.Repository.user_id == uid:
369 p = 'repository.admin'
370 else:
371 p = perm.Permission.permission_name
372 if not explicit:
373 p = _choose_perm(p, cur_perm)
374 permissions[RK][r_k] = p
375
376 #======================================================================
377 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
378 #======================================================================
379 #======================================================================
380 # check if user is part of user groups for this repository groups and
381 # fill in his permission from it. _choose_perm decides of which
382 # permission should be selected based on selected method
383 #======================================================================
384 # user group for repo groups permissions
385 user_repo_group_perms_from_users_groups = \
386 Session().query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
387 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
388 .join((Permission, UserGroupRepoGroupToPerm.permission_id
389 == Permission.permission_id))\
390 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
391 == UserGroupMember.users_group_id))\
392 .filter(UserGroupMember.user_id == uid)\
393 .all()
394
395 multiple_counter = collections.defaultdict(int)
396 for perm in user_repo_group_perms_from_users_groups:
397 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
398 multiple_counter[g_k] += 1
399 p = perm.Permission.permission_name
400 cur_perm = permissions[GK][g_k]
401 if multiple_counter[g_k] > 1:
402 p = _choose_perm(p, cur_perm)
403 permissions[GK][g_k] = p
404
405 # user explicit permissions for repository groups
406 user_repo_groups_perms = Permission.get_default_group_perms(uid)
407 for perm in user_repo_groups_perms:
408 rg_k = perm.UserRepoGroupToPerm.group.group_name
409 p = perm.Permission.permission_name
410 cur_perm = permissions[GK][rg_k]
411 if not explicit:
412 p = _choose_perm(p, cur_perm)
413 permissions[GK][rg_k] = p
414
415 #======================================================================
416 # !! PERMISSIONS FOR USER GROUPS !!
417 #======================================================================
418 # user group for user group permissions
419 user_group_user_groups_perms = \
420 Session().query(UserGroupUserGroupToPerm, Permission, UserGroup)\
421 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
422 == UserGroup.users_group_id))\
423 .join((Permission, UserGroupUserGroupToPerm.permission_id
424 == Permission.permission_id))\
425 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
426 == UserGroupMember.users_group_id))\
427 .filter(UserGroupMember.user_id == uid)\
428 .all()
429
430 multiple_counter = collections.defaultdict(int)
431 for perm in user_group_user_groups_perms:
432 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
433 multiple_counter[g_k] += 1
434 p = perm.Permission.permission_name
435 cur_perm = permissions[UK][g_k]
436 if multiple_counter[g_k] > 1:
437 p = _choose_perm(p, cur_perm)
438 permissions[UK][g_k] = p
439
440 #user explicit permission for user groups
441 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
442 for perm in user_user_groups_perms:
443 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
444 p = perm.Permission.permission_name
445 cur_perm = permissions[UK][u_k]
446 if not explicit:
447 p = _choose_perm(p, cur_perm)
448 permissions[UK][u_k] = p
449
450 return permissions
451
452
453 def allowed_api_access(controller_name, whitelist=None, api_key=None):
454 """
455 Check if given controller_name is in whitelist API access
456 """
457 if not whitelist:
458 from rhodecode import CONFIG
459 whitelist = aslist(CONFIG.get('api_access_controllers_whitelist'),
460 sep=',')
461 log.debug('whitelist of API access is: %s' % (whitelist))
462 api_access_valid = controller_name in whitelist
463 if api_access_valid:
464 log.debug('controller:%s is in API whitelist' % (controller_name))
465 else:
466 msg = 'controller: %s is *NOT* in API whitelist' % (controller_name)
467 if api_key:
468 #if we use API key and don't have access it's a warning
469 log.warning(msg)
470 else:
471 log.debug(msg)
472 return api_access_valid
473
474
475 class AuthUser(object):
328 """ 476 """
329 A simple object that handles all attributes of user in RhodeCode 477 A simple object that handles all attributes of user in RhodeCode
330 478
331 It does lookup based on API key,given user, or user present in session 479 It does lookup based on API key,given user, or user present in session
332 Then it fills all required information for such user. It also checks if 480 Then it fills all required information for such user. It also checks if
333 anonymous access is enabled and if so, it returns default user as logged 481 anonymous access is enabled and if so, it returns default user as logged in
334 in
335 """ 482 """
336 483
337 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None): 484 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
338 485
339 self.user_id = user_id 486 self.user_id = user_id
487 self._api_key = api_key
488
340 self.api_key = None 489 self.api_key = None
341 self.username = username 490 self.username = username
342 self.ip_addr = ip_addr 491 self.ip_addr = ip_addr
343
344 self.name = '' 492 self.name = ''
345 self.lastname = '' 493 self.lastname = ''
346 self.email = '' 494 self.email = ''
347 self.is_authenticated = False 495 self.is_authenticated = False
348 self.admin = False 496 self.admin = False
349 self.inherit_default_permissions = False 497 self.inherit_default_permissions = False
350 self.permissions = {} 498
351 self._api_key = api_key
352 self.propagate_data() 499 self.propagate_data()
353 self._instance = None 500 self._instance = None
354 501
502 @LazyProperty
503 def permissions(self):
504 return self.get_perms(user=self, cache=False)
505
506 @property
507 def api_keys(self):
508 return self.get_api_keys()
509
355 def propagate_data(self): 510 def propagate_data(self):
356 user_model = UserModel() 511 user_model = UserModel()
357 self.anonymous_user = User.get_by_username('default', cache=True) 512 self.anonymous_user = User.get_default_user(cache=True)
358 is_user_loaded = False 513 is_user_loaded = False
359 514
515 # lookup by userid
516 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
517 log.debug('Auth User lookup by USER ID %s' % self.user_id)
518 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
519
360 # try go get user by api key 520 # try go get user by api key
361 if self._api_key and self._api_key != self.anonymous_user.api_key: 521 elif self._api_key and self._api_key != self.anonymous_user.api_key:
362 log.debug('Auth User lookup by API KEY %s' % self._api_key) 522 log.debug('Auth User lookup by API KEY %s' % self._api_key)
363 is_user_loaded = user_model.fill_data(self, api_key=self._api_key) 523 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
364 # lookup by userid 524
365 elif (self.user_id is not None and
366 self.user_id != self.anonymous_user.user_id):
367 log.debug('Auth User lookup by USER ID %s' % self.user_id)
368 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
369 # lookup by username 525 # lookup by username
370 elif self.username and \ 526 elif self.username:
371 str2bool(config.get('container_auth_enabled', False)):
372
373 log.debug('Auth User lookup by USER NAME %s' % self.username) 527 log.debug('Auth User lookup by USER NAME %s' % self.username)
374 dbuser = login_container_auth(self.username) 528 is_user_loaded = user_model.fill_data(self, username=self.username)
375 if dbuser is not None:
376 log.debug('filling all attributes to object')
377 for k, v in dbuser.get_dict().items():
378 setattr(self, k, v)
379 self.set_authenticated()
380 is_user_loaded = True
381 else: 529 else:
382 log.debug('No data in %s that could been used to log in' % self) 530 log.debug('No data in %s that could been used to log in' % self)
383 531
384 if not is_user_loaded: 532 if not is_user_loaded:
385 # if we cannot authenticate user try anonymous 533 # if we cannot authenticate user try anonymous
394 542
395 if not self.username: 543 if not self.username:
396 self.username = 'None' 544 self.username = 'None'
397 545
398 log.debug('Auth User is now %s' % self) 546 log.debug('Auth User is now %s' % self)
399 user_model.fill_perms(self) 547
548 def get_perms(self, user, explicit=True, algo='higherwin', cache=False):
549 """
550 Fills user permission attribute with permissions taken from database
551 works for permissions given for repositories, and for permissions that
552 are granted to groups
553
554 :param user: instance of User object from database
555 :param explicit: In case there are permissions both for user and a group
556 that user is part of, explicit flag will defiine if user will
557 explicitly override permissions from group, if it's False it will
558 make decision based on the algo
559 :param algo: algorithm to decide what permission should be choose if
560 it's multiple defined, eg user in two different groups. It also
561 decides if explicit flag is turned off how to specify the permission
562 for case when user is in a group + have defined separate permission
563 """
564 user_id = user.user_id
565 user_is_admin = user.is_admin
566 user_inherit_default_permissions = user.inherit_default_permissions
567
568 log.debug('Getting PERMISSION tree')
569 compute = conditional_cache('short_term', 'cache_desc',
570 condition=cache, func=_cached_perms_data)
571 return compute(user_id, user_is_admin,
572 user_inherit_default_permissions, explicit, algo)
573
574 def get_api_keys(self):
575 api_keys = [self.api_key]
576 for api_key in UserApiKeys.query()\
577 .filter(UserApiKeys.user_id == self.user_id)\
578 .filter(or_(UserApiKeys.expires == -1,
579 UserApiKeys.expires >= time.time())).all():
580 api_keys.append(api_key.api_key)
581
582 return api_keys
400 583
401 @property 584 @property
402 def is_admin(self): 585 def is_admin(self):
403 return self.admin 586 return self.admin
404 587
432 Checks if ip_addr used in constructor is allowed from defined list of 615 Checks if ip_addr used in constructor is allowed from defined list of
433 allowed ip_addresses for user 616 allowed ip_addresses for user
434 617
435 :returns: boolean, True if ip is in allowed ip range 618 :returns: boolean, True if ip is in allowed ip range
436 """ 619 """
437 #check IP 620 # check IP
438 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True) 621 inherit = self.inherit_default_permissions
439 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips): 622 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
440 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips)) 623 inherit_from_default=inherit)
624
625 @classmethod
626 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
627 allowed_ips = AuthUser.get_allowed_ips(user_id, cache=True,
628 inherit_from_default=inherit_from_default)
629 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
630 log.debug('IP:%s is in range of %s' % (ip_addr, allowed_ips))
441 return True 631 return True
442 else: 632 else:
443 log.info('Access for IP:%s forbidden, ' 633 log.info('Access for IP:%s forbidden, '
444 'not in %s' % (self.ip_addr, allowed_ips)) 634 'not in %s' % (ip_addr, allowed_ips))
445 return False 635 return False
446 636
447 def __repr__(self): 637 def __repr__(self):
448 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\ 638 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
449 % (self.user_id, self.username, self.ip_addr, self.is_authenticated) 639 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
469 username = cookie_store.get('username') 659 username = cookie_store.get('username')
470 api_key = cookie_store.get('api_key') 660 api_key = cookie_store.get('api_key')
471 return AuthUser(user_id, api_key, username) 661 return AuthUser(user_id, api_key, username)
472 662
473 @classmethod 663 @classmethod
474 def get_allowed_ips(cls, user_id, cache=False): 664 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
475 _set = set() 665 _set = set()
666
667 if inherit_from_default:
668 default_ips = UserIpMap.query().filter(UserIpMap.user ==
669 User.get_default_user(cache=True))
670 if cache:
671 default_ips = default_ips.options(FromCache("sql_cache_short",
672 "get_user_ips_default"))
673
674 # populate from default user
675 for ip in default_ips:
676 try:
677 _set.add(ip.ip_addr)
678 except ObjectDeletedError:
679 # since we use heavy caching sometimes it happens that we get
680 # deleted objects here, we just skip them
681 pass
682
476 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id) 683 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
477 if cache: 684 if cache:
478 user_ips = user_ips.options(FromCache("sql_cache_short", 685 user_ips = user_ips.options(FromCache("sql_cache_short",
479 "get_user_ips_%s" % user_id)) 686 "get_user_ips_%s" % user_id))
687
480 for ip in user_ips: 688 for ip in user_ips:
481 try: 689 try:
482 _set.add(ip.ip_addr) 690 _set.add(ip.ip_addr)
483 except ObjectDeletedError: 691 except ObjectDeletedError:
484 # since we use heavy caching sometimes it happens that we get 692 # since we use heavy caching sometimes it happens that we get
528 736
529 def __wrapper(self, func, *fargs, **fkwargs): 737 def __wrapper(self, func, *fargs, **fkwargs):
530 cls = fargs[0] 738 cls = fargs[0]
531 user = cls.rhodecode_user 739 user = cls.rhodecode_user
532 loc = "%s:%s" % (cls.__class__.__name__, func.__name__) 740 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
533 # defined whitelist of controllers which API access will be enabled 741
534 whitelist = aslist(config.get('api_access_controllers_whitelist'), 742 # check if our IP is allowed
535 sep=',') 743 ip_access_valid = True
536 api_access_whitelist = loc in whitelist
537 log.debug('loc:%s is in API whitelist:%s:%s' % (loc, whitelist,
538 api_access_whitelist))
539 #check IP
540 ip_access_ok = True
541 if not user.ip_allowed: 744 if not user.ip_allowed:
542 from rhodecode.lib import helpers as h 745 from rhodecode.lib import helpers as h
543 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))), 746 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
544 category='warning') 747 category='warning')
545 ip_access_ok = False 748 ip_access_valid = False
546 749
547 api_access_ok = False 750 # check if we used an APIKEY and it's a valid one
548 if self.api_access or api_access_whitelist: 751 # defined whitelist of controllers which API access will be enabled
752 _api_key = request.GET.get('api_key', '')
753 api_access_valid = allowed_api_access(loc, api_key=_api_key)
754
755 # explicit controller is enabled or API is in our whitelist
756 if self.api_access or api_access_valid:
549 log.debug('Checking API KEY access for %s' % cls) 757 log.debug('Checking API KEY access for %s' % cls)
550 if user.api_key == request.GET.get('api_key'): 758 if _api_key and _api_key in user.api_keys:
551 api_access_ok = True 759 api_access_valid = True
760 log.debug('API KEY ****%s is VALID' % _api_key[-4:])
552 else: 761 else:
553 log.debug("API KEY token not valid") 762 api_access_valid = False
763 if not _api_key:
764 log.debug("API KEY *NOT* present in request")
765 else:
766 log.warn("API KEY ****%s *NOT* valid" % _api_key[-4:])
554 767
555 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc)) 768 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
556 if (user.is_authenticated or api_access_ok) and ip_access_ok: 769 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
557 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth' 770
558 log.info('user %s is authenticated and granted access to %s ' 771 if ip_access_valid and (user.is_authenticated or api_access_valid):
559 'using %s' % (user.username, loc, reason) 772 log.info('user %s authenticating with:%s IS authenticated on func %s '
773 % (user, reason, loc)
560 ) 774 )
561 return func(*fargs, **fkwargs) 775 return func(*fargs, **fkwargs)
562 else: 776 else:
563 log.warn('user %s NOT authenticated on func: %s' % ( 777 log.warn('user %s authenticating with:%s NOT authenticated on func: %s: '
564 user, loc) 778 'IP_ACCESS:%s API_ACCESS:%s'
779 % (user, reason, loc, ip_access_valid, api_access_valid)
565 ) 780 )
566 p = url.current() 781 p = url.current()
567 782
568 log.debug('redirecting to login page with %s' % p) 783 log.debug('redirecting to login page with %s' % p)
569 return redirect(url('login_home', came_from=p)) 784 return redirect(url('login_home', came_from=p))
581 cls = fargs[0] 796 cls = fargs[0]
582 self.user = cls.rhodecode_user 797 self.user = cls.rhodecode_user
583 798
584 log.debug('Checking if user is not anonymous @%s' % cls) 799 log.debug('Checking if user is not anonymous @%s' % cls)
585 800
586 anonymous = self.user.username == 'default' 801 anonymous = self.user.username == User.DEFAULT_USER
587 802
588 if anonymous: 803 if anonymous:
589 p = url.current() 804 p = url.current()
590 805
591 import rhodecode.lib.helpers as h 806 import rhodecode.lib.helpers as h
599 814
600 class PermsDecorator(object): 815 class PermsDecorator(object):
601 """Base class for controller decorators""" 816 """Base class for controller decorators"""
602 817
603 def __init__(self, *required_perms): 818 def __init__(self, *required_perms):
604 available_perms = config['available_permissions']
605 for perm in required_perms:
606 if perm not in available_perms:
607 raise Exception("'%s' permission is not defined" % perm)
608 self.required_perms = set(required_perms) 819 self.required_perms = set(required_perms)
609 self.user_perms = None 820 self.user_perms = None
610 821
611 def __call__(self, func): 822 def __call__(self, func):
612 return decorator(self.__wrapper, func) 823 return decorator(self.__wrapper, func)
622 log.debug('Permission granted for %s %s' % (cls, self.user)) 833 log.debug('Permission granted for %s %s' % (cls, self.user))
623 return func(*fargs, **fkwargs) 834 return func(*fargs, **fkwargs)
624 835
625 else: 836 else:
626 log.debug('Permission denied for %s %s' % (cls, self.user)) 837 log.debug('Permission denied for %s %s' % (cls, self.user))
627 anonymous = self.user.username == 'default' 838 anonymous = self.user.username == User.DEFAULT_USER
628 839
629 if anonymous: 840 if anonymous:
630 p = url.current() 841 p = url.current()
631 842
632 import rhodecode.lib.helpers as h 843 import rhodecode.lib.helpers as h
701 if self.required_perms.intersection(user_perms): 912 if self.required_perms.intersection(user_perms):
702 return True 913 return True
703 return False 914 return False
704 915
705 916
706 class HasReposGroupPermissionAllDecorator(PermsDecorator): 917 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
707 """ 918 """
708 Checks for access permission for all given predicates for specific 919 Checks for access permission for all given predicates for specific
709 repository group. All of them have to be meet in order to fulfill the request 920 repository group. All of them have to be meet in order to fulfill the request
710 """ 921 """
711 922
712 def check_permissions(self): 923 def check_permissions(self):
713 group_name = get_repos_group_slug(request) 924 group_name = get_repo_group_slug(request)
714 try: 925 try:
715 user_perms = set([self.user_perms['repositories_groups'][group_name]]) 926 user_perms = set([self.user_perms['repositories_groups'][group_name]])
716 except KeyError: 927 except KeyError:
717 return False 928 return False
718 929
719 if self.required_perms.issubset(user_perms): 930 if self.required_perms.issubset(user_perms):
720 return True 931 return True
721 return False 932 return False
722 933
723 934
724 class HasReposGroupPermissionAnyDecorator(PermsDecorator): 935 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
725 """ 936 """
726 Checks for access permission for any of given predicates for specific 937 Checks for access permission for any of given predicates for specific
727 repository group. In order to fulfill the request any of predicates must be meet 938 repository group. In order to fulfill the request any of predicates must be meet
728 """ 939 """
729 940
730 def check_permissions(self): 941 def check_permissions(self):
731 group_name = get_repos_group_slug(request) 942 group_name = get_repo_group_slug(request)
732 try: 943 try:
733 user_perms = set([self.user_perms['repositories_groups'][group_name]]) 944 user_perms = set([self.user_perms['repositories_groups'][group_name]])
734 except KeyError: 945 except KeyError:
735 return False 946 return False
736 947
780 #============================================================================== 991 #==============================================================================
781 class PermsFunction(object): 992 class PermsFunction(object):
782 """Base function for other check functions""" 993 """Base function for other check functions"""
783 994
784 def __init__(self, *perms): 995 def __init__(self, *perms):
785 available_perms = config['available_permissions']
786
787 for perm in perms:
788 if perm not in available_perms:
789 raise Exception("'%s' permission is not defined" % perm)
790 self.required_perms = set(perms) 996 self.required_perms = set(perms)
791 self.user_perms = None 997 self.user_perms = None
792 self.repo_name = None 998 self.repo_name = None
793 self.group_name = None 999 self.group_name = None
794 1000
795 def __call__(self, check_location=''): 1001 def __call__(self, check_location='', user=None):
796 #TODO: put user as attribute here 1002 if not user:
797 user = request.user 1003 #TODO: remove this someday,put as user as attribute here
1004 user = request.user
1005
1006 # init auth user if not already given
1007 if not isinstance(user, AuthUser):
1008 user = AuthUser(user.user_id)
1009
798 cls_name = self.__class__.__name__ 1010 cls_name = self.__class__.__name__
799 check_scope = { 1011 check_scope = {
800 'HasPermissionAll': '', 1012 'HasPermissionAll': '',
801 'HasPermissionAny': '', 1013 'HasPermissionAny': '',
802 'HasRepoPermissionAll': 'repo:%s' % self.repo_name, 1014 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
803 'HasRepoPermissionAny': 'repo:%s' % self.repo_name, 1015 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
804 'HasReposGroupPermissionAll': 'group:%s' % self.group_name, 1016 'HasRepoGroupPermissionAll': 'group:%s' % self.group_name,
805 'HasReposGroupPermissionAny': 'group:%s' % self.group_name, 1017 'HasRepoGroupPermissionAny': 'group:%s' % self.group_name,
806 }.get(cls_name, '?') 1018 }.get(cls_name, '?')
807 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name, 1019 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
808 self.required_perms, user, check_scope, 1020 self.required_perms, user, check_scope,
809 check_location or 'unspecified location') 1021 check_location or 'unspecified location')
810 if not user: 1022 if not user:
811 log.debug('Empty request user') 1023 log.debug('Empty request user')
812 return False 1024 return False
813 self.user_perms = user.permissions 1025 self.user_perms = user.permissions
814 if self.check_permissions(): 1026 if self.check_permissions():
815 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user, 1027 log.debug('Permission to %s granted for user: %s @ %s'
816 check_location or 'unspecified location') 1028 % (check_scope, user,
1029 check_location or 'unspecified location'))
817 return True 1030 return True
818 1031
819 else: 1032 else:
820 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user, 1033 log.debug('Permission to %s denied for user: %s @ %s'
821 check_location or 'unspecified location') 1034 % (check_scope, user,
1035 check_location or 'unspecified location'))
822 return False 1036 return False
823 1037
824 def check_permissions(self): 1038 def check_permissions(self):
825 """Dummy function for overriding""" 1039 """Dummy function for overriding"""
826 raise Exception('You have to write this function in child class') 1040 raise Exception('You have to write this function in child class')
839 return True 1053 return True
840 return False 1054 return False
841 1055
842 1056
843 class HasRepoPermissionAll(PermsFunction): 1057 class HasRepoPermissionAll(PermsFunction):
844 def __call__(self, repo_name=None, check_location=''): 1058 def __call__(self, repo_name=None, check_location='', user=None):
845 self.repo_name = repo_name 1059 self.repo_name = repo_name
846 return super(HasRepoPermissionAll, self).__call__(check_location) 1060 return super(HasRepoPermissionAll, self).__call__(check_location, user)
847 1061
848 def check_permissions(self): 1062 def check_permissions(self):
849 if not self.repo_name: 1063 if not self.repo_name:
850 self.repo_name = get_repo_slug(request) 1064 self.repo_name = get_repo_slug(request)
851 1065
859 return True 1073 return True
860 return False 1074 return False
861 1075
862 1076
863 class HasRepoPermissionAny(PermsFunction): 1077 class HasRepoPermissionAny(PermsFunction):
864 def __call__(self, repo_name=None, check_location=''): 1078 def __call__(self, repo_name=None, check_location='', user=None):
865 self.repo_name = repo_name 1079 self.repo_name = repo_name
866 return super(HasRepoPermissionAny, self).__call__(check_location) 1080 return super(HasRepoPermissionAny, self).__call__(check_location, user)
867 1081
868 def check_permissions(self): 1082 def check_permissions(self):
869 if not self.repo_name: 1083 if not self.repo_name:
870 self.repo_name = get_repo_slug(request) 1084 self.repo_name = get_repo_slug(request)
871 1085
878 if self.required_perms.intersection(self._user_perms): 1092 if self.required_perms.intersection(self._user_perms):
879 return True 1093 return True
880 return False 1094 return False
881 1095
882 1096
883 class HasReposGroupPermissionAny(PermsFunction): 1097 class HasRepoGroupPermissionAny(PermsFunction):
884 def __call__(self, group_name=None, check_location=''): 1098 def __call__(self, group_name=None, check_location='', user=None):
885 self.group_name = group_name 1099 self.group_name = group_name
886 return super(HasReposGroupPermissionAny, self).__call__(check_location) 1100 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
887 1101
888 def check_permissions(self): 1102 def check_permissions(self):
889 try: 1103 try:
890 self._user_perms = set( 1104 self._user_perms = set(
891 [self.user_perms['repositories_groups'][self.group_name]] 1105 [self.user_perms['repositories_groups'][self.group_name]]
895 if self.required_perms.intersection(self._user_perms): 1109 if self.required_perms.intersection(self._user_perms):
896 return True 1110 return True
897 return False 1111 return False
898 1112
899 1113
900 class HasReposGroupPermissionAll(PermsFunction): 1114 class HasRepoGroupPermissionAll(PermsFunction):
901 def __call__(self, group_name=None, check_location=''): 1115 def __call__(self, group_name=None, check_location='', user=None):
902 self.group_name = group_name 1116 self.group_name = group_name
903 return super(HasReposGroupPermissionAll, self).__call__(check_location) 1117 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
904 1118
905 def check_permissions(self): 1119 def check_permissions(self):
906 try: 1120 try:
907 self._user_perms = set( 1121 self._user_perms = set(
908 [self.user_perms['repositories_groups'][self.group_name]] 1122 [self.user_perms['repositories_groups'][self.group_name]]
913 return True 1127 return True
914 return False 1128 return False
915 1129
916 1130
917 class HasUserGroupPermissionAny(PermsFunction): 1131 class HasUserGroupPermissionAny(PermsFunction):
918 def __call__(self, user_group_name=None, check_location=''): 1132 def __call__(self, user_group_name=None, check_location='', user=None):
919 self.user_group_name = user_group_name 1133 self.user_group_name = user_group_name
920 return super(HasUserGroupPermissionAny, self).__call__(check_location) 1134 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
921 1135
922 def check_permissions(self): 1136 def check_permissions(self):
923 try: 1137 try:
924 self._user_perms = set( 1138 self._user_perms = set(
925 [self.user_perms['user_groups'][self.user_group_name]] 1139 [self.user_perms['user_groups'][self.user_group_name]]
930 return True 1144 return True
931 return False 1145 return False
932 1146
933 1147
934 class HasUserGroupPermissionAll(PermsFunction): 1148 class HasUserGroupPermissionAll(PermsFunction):
935 def __call__(self, user_group_name=None, check_location=''): 1149 def __call__(self, user_group_name=None, check_location='', user=None):
936 self.user_group_name = user_group_name 1150 self.user_group_name = user_group_name
937 return super(HasUserGroupPermissionAll, self).__call__(check_location) 1151 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
938 1152
939 def check_permissions(self): 1153 def check_permissions(self):
940 try: 1154 try:
941 self._user_perms = set( 1155 self._user_perms = set(
942 [self.user_perms['user_groups'][self.user_group_name]] 1156 [self.user_perms['user_groups'][self.user_group_name]]
944 except KeyError: 1158 except KeyError:
945 return False 1159 return False
946 if self.required_perms.issubset(self._user_perms): 1160 if self.required_perms.issubset(self._user_perms):
947 return True 1161 return True
948 return False 1162 return False
1163
949 1164
950 #============================================================================== 1165 #==============================================================================
951 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH 1166 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
952 #============================================================================== 1167 #==============================================================================
953 class HasPermissionAnyMiddleware(object): 1168 class HasPermissionAnyMiddleware(object):
972 def check_permissions(self): 1187 def check_permissions(self):
973 log.debug('checking VCS protocol ' 1188 log.debug('checking VCS protocol '
974 'permissions %s for user:%s repository:%s', self.user_perms, 1189 'permissions %s for user:%s repository:%s', self.user_perms,
975 self.username, self.repo_name) 1190 self.username, self.repo_name)
976 if self.required_perms.intersection(self.user_perms): 1191 if self.required_perms.intersection(self.user_perms):
977 log.debug('permission granted for user:%s on repo:%s' % ( 1192 log.debug('Permission to repo: %s granted for user: %s @ %s'
978 self.username, self.repo_name 1193 % (self.repo_name, self.username, 'PermissionMiddleware'))
979 ) 1194 return True
980 ) 1195 log.debug('Permission to repo: %s denied for user: %s @ %s'
981 return True 1196 % (self.repo_name, self.username, 'PermissionMiddleware'))
982 log.debug('permission denied for user:%s on repo:%s' % (
983 self.username, self.repo_name
984 )
985 )
986 return False 1197 return False
987 1198
988 1199
989 #============================================================================== 1200 #==============================================================================
990 # SPECIAL VERSION TO HANDLE API AUTH 1201 # SPECIAL VERSION TO HANDLE API AUTH
991 #============================================================================== 1202 #==============================================================================
992 class _BaseApiPerm(object): 1203 class _BaseApiPerm(object):
993 def __init__(self, *perms): 1204 def __init__(self, *perms):
994 self.required_perms = set(perms) 1205 self.required_perms = set(perms)
995 1206
996 def __call__(self, check_location='unspecified', user=None, repo_name=None): 1207 def __call__(self, check_location=None, user=None, repo_name=None,
1208 group_name=None):
997 cls_name = self.__class__.__name__ 1209 cls_name = self.__class__.__name__
998 check_scope = 'user:%s, repo:%s' % (user, repo_name) 1210 check_scope = 'user:%s' % (user)
999 log.debug('checking cls:%s %s %s @ %s', cls_name, 1211 if repo_name:
1000 self.required_perms, check_scope, check_location) 1212 check_scope += ', repo:%s' % (repo_name)
1213
1214 if group_name:
1215 check_scope += ', repo group:%s' % (group_name)
1216
1217 log.debug('checking cls:%s %s %s @ %s'
1218 % (cls_name, self.required_perms, check_scope, check_location))
1001 if not user: 1219 if not user:
1002 log.debug('Empty User passed into arguments') 1220 log.debug('Empty User passed into arguments')
1003 return False 1221 return False
1004 1222
1005 ## process user 1223 ## process user
1006 if not isinstance(user, AuthUser): 1224 if not isinstance(user, AuthUser):
1007 user = AuthUser(user.user_id) 1225 user = AuthUser(user.user_id)
1008 1226 if not check_location:
1009 if self.check_permissions(user.permissions, repo_name): 1227 check_location = 'unspecified'
1010 log.debug('Permission to %s granted for user: %s @ %s', repo_name, 1228 if self.check_permissions(user.permissions, repo_name, group_name):
1011 user, check_location) 1229 log.debug('Permission to %s granted for user: %s @ %s'
1230 % (check_scope, user, check_location))
1012 return True 1231 return True
1013 1232
1014 else: 1233 else:
1015 log.debug('Permission to %s denied for user: %s @ %s', repo_name, 1234 log.debug('Permission to %s denied for user: %s @ %s'
1016 user, check_location) 1235 % (check_scope, user, check_location))
1017 return False 1236 return False
1018 1237
1019 def check_permissions(self, perm_defs, repo_name): 1238 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1020 """ 1239 """
1021 implement in child class should return True if permissions are ok, 1240 implement in child class should return True if permissions are ok,
1022 False otherwise 1241 False otherwise
1023 1242
1024 :param perm_defs: dict with permission definitions 1243 :param perm_defs: dict with permission definitions
1026 """ 1245 """
1027 raise NotImplementedError() 1246 raise NotImplementedError()
1028 1247
1029 1248
1030 class HasPermissionAllApi(_BaseApiPerm): 1249 class HasPermissionAllApi(_BaseApiPerm):
1031 def __call__(self, user, check_location=''): 1250 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1032 return super(HasPermissionAllApi, self)\
1033 .__call__(check_location=check_location, user=user)
1034
1035 def check_permissions(self, perm_defs, repo):
1036 if self.required_perms.issubset(perm_defs.get('global')): 1251 if self.required_perms.issubset(perm_defs.get('global')):
1037 return True 1252 return True
1038 return False 1253 return False
1039 1254
1040 1255
1041 class HasPermissionAnyApi(_BaseApiPerm): 1256 class HasPermissionAnyApi(_BaseApiPerm):
1042 def __call__(self, user, check_location=''): 1257 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1043 return super(HasPermissionAnyApi, self)\
1044 .__call__(check_location=check_location, user=user)
1045
1046 def check_permissions(self, perm_defs, repo):
1047 if self.required_perms.intersection(perm_defs.get('global')): 1258 if self.required_perms.intersection(perm_defs.get('global')):
1048 return True 1259 return True
1049 return False 1260 return False
1050 1261
1051 1262
1052 class HasRepoPermissionAllApi(_BaseApiPerm): 1263 class HasRepoPermissionAllApi(_BaseApiPerm):
1053 def __call__(self, user, repo_name, check_location=''): 1264 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1054 return super(HasRepoPermissionAllApi, self)\ 1265 try:
1055 .__call__(check_location=check_location, user=user, 1266 _user_perms = set([perm_defs['repositories'][repo_name]])
1056 repo_name=repo_name)
1057
1058 def check_permissions(self, perm_defs, repo_name):
1059
1060 try:
1061 self._user_perms = set(
1062 [perm_defs['repositories'][repo_name]]
1063 )
1064 except KeyError: 1267 except KeyError:
1065 log.warning(traceback.format_exc()) 1268 log.warning(traceback.format_exc())
1066 return False 1269 return False
1067 if self.required_perms.issubset(self._user_perms): 1270 if self.required_perms.issubset(_user_perms):
1068 return True 1271 return True
1069 return False 1272 return False
1070 1273
1071 1274
1072 class HasRepoPermissionAnyApi(_BaseApiPerm): 1275 class HasRepoPermissionAnyApi(_BaseApiPerm):
1073 def __call__(self, user, repo_name, check_location=''): 1276 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1074 return super(HasRepoPermissionAnyApi, self)\ 1277 try:
1075 .__call__(check_location=check_location, user=user, 1278 _user_perms = set([perm_defs['repositories'][repo_name]])
1076 repo_name=repo_name)
1077
1078 def check_permissions(self, perm_defs, repo_name):
1079
1080 try:
1081 _user_perms = set(
1082 [perm_defs['repositories'][repo_name]]
1083 )
1084 except KeyError: 1279 except KeyError:
1085 log.warning(traceback.format_exc()) 1280 log.warning(traceback.format_exc())
1086 return False 1281 return False
1087 if self.required_perms.intersection(_user_perms): 1282 if self.required_perms.intersection(_user_perms):
1088 return True 1283 return True
1089 return False 1284 return False
1090 1285
1286
1287 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
1288 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1289 try:
1290 _user_perms = set([perm_defs['repositories_groups'][group_name]])
1291 except KeyError:
1292 log.warning(traceback.format_exc())
1293 return False
1294 if self.required_perms.intersection(_user_perms):
1295 return True
1296 return False
1297
1298 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
1299 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1300 try:
1301 _user_perms = set([perm_defs['repositories_groups'][group_name]])
1302 except KeyError:
1303 log.warning(traceback.format_exc())
1304 return False
1305 if self.required_perms.issubset(_user_perms):
1306 return True
1307 return False
1091 1308
1092 def check_ip_access(source_ip, allowed_ips=None): 1309 def check_ip_access(source_ip, allowed_ips=None):
1093 """ 1310 """
1094 Checks if source_ip is a subnet of any of allowed_ips. 1311 Checks if source_ip is a subnet of any of allowed_ips.
1095 1312
1100 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips)) 1317 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1101 if isinstance(allowed_ips, (tuple, list, set)): 1318 if isinstance(allowed_ips, (tuple, list, set)):
1102 for ip in allowed_ips: 1319 for ip in allowed_ips:
1103 try: 1320 try:
1104 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip): 1321 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1322 log.debug('IP %s is network %s' %
1323 (ipaddr.IPAddress(source_ip), ipaddr.IPNetwork(ip)))
1105 return True 1324 return True
1106 # for any case we cannot determine the IP, don't crash just 1325 # for any case we cannot determine the IP, don't crash just
1107 # skip it and log as error, we want to say forbidden still when 1326 # skip it and log as error, we want to say forbidden still when
1108 # sending bad IP 1327 # sending bad IP
1109 except Exception: 1328 except Exception: