Mercurial > kallithea
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: |