##// END OF EJS Templates
auth: return early in LoginRequired on invalid IP...
Thomas De Schampheleire -
r5115:4cad3a52 default
parent child Browse files
Show More
@@ -1,1326 +1,1315 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # 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
3 # 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
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # 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
13 # 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/>.
14 """
14 """
15 kallithea.lib.auth
15 kallithea.lib.auth
16 ~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~
17
17
18 authentication and permission libraries
18 authentication and permission libraries
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Apr 4, 2010
22 :created_on: Apr 4, 2010
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27 from __future__ import with_statement
27 from __future__ import with_statement
28 import time
28 import time
29 import random
29 import random
30 import logging
30 import logging
31 import traceback
31 import traceback
32 import hashlib
32 import hashlib
33 import itertools
33 import itertools
34 import collections
34 import collections
35
35
36 from tempfile import _RandomNameSequence
36 from tempfile import _RandomNameSequence
37 from decorator import decorator
37 from decorator import decorator
38
38
39 from pylons import url, request
39 from pylons import url, request
40 from pylons.controllers.util import abort, redirect
40 from pylons.controllers.util import abort, redirect
41 from pylons.i18n.translation import _
41 from pylons.i18n.translation import _
42 from webhelpers.pylonslib import secure_form
42 from webhelpers.pylonslib import secure_form
43 from sqlalchemy import or_
43 from sqlalchemy import or_
44 from sqlalchemy.orm.exc import ObjectDeletedError
44 from sqlalchemy.orm.exc import ObjectDeletedError
45 from sqlalchemy.orm import joinedload
45 from sqlalchemy.orm import joinedload
46
46
47 from kallithea import __platform__, is_windows, is_unix
47 from kallithea import __platform__, is_windows, is_unix
48 from kallithea.lib.vcs.utils.lazy import LazyProperty
48 from kallithea.lib.vcs.utils.lazy import LazyProperty
49 from kallithea.model import meta
49 from kallithea.model import meta
50 from kallithea.model.meta import Session
50 from kallithea.model.meta import Session
51 from kallithea.model.user import UserModel
51 from kallithea.model.user import UserModel
52 from kallithea.model.db import User, Repository, Permission, \
52 from kallithea.model.db import User, Repository, Permission, \
53 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
53 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
54 RepoGroup, UserGroupRepoGroupToPerm, UserIpMap, UserGroupUserGroupToPerm, \
54 RepoGroup, UserGroupRepoGroupToPerm, UserIpMap, UserGroupUserGroupToPerm, \
55 UserGroup, UserApiKeys
55 UserGroup, UserApiKeys
56
56
57 from kallithea.lib.utils2 import safe_unicode, aslist
57 from kallithea.lib.utils2 import safe_unicode, aslist
58 from kallithea.lib.utils import get_repo_slug, get_repo_group_slug, \
58 from kallithea.lib.utils import get_repo_slug, get_repo_group_slug, \
59 get_user_group_slug, conditional_cache
59 get_user_group_slug, conditional_cache
60 from kallithea.lib.caching_query import FromCache
60 from kallithea.lib.caching_query import FromCache
61
61
62
62
63 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
64
64
65
65
66 class PasswordGenerator(object):
66 class PasswordGenerator(object):
67 """
67 """
68 This is a simple class for generating password from different sets of
68 This is a simple class for generating password from different sets of
69 characters
69 characters
70 usage::
70 usage::
71
71
72 passwd_gen = PasswordGenerator()
72 passwd_gen = PasswordGenerator()
73 #print 8-letter password containing only big and small letters
73 #print 8-letter password containing only big and small letters
74 of alphabet
74 of alphabet
75 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
75 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
76 """
76 """
77 ALPHABETS_NUM = r'''1234567890'''
77 ALPHABETS_NUM = r'''1234567890'''
78 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
78 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
79 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
79 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
80 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
80 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
81 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
81 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
82 + ALPHABETS_NUM + ALPHABETS_SPECIAL
82 + ALPHABETS_NUM + ALPHABETS_SPECIAL
83 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
83 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
84 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
84 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
85 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
85 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
86 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
86 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
87
87
88 def __init__(self, passwd=''):
88 def __init__(self, passwd=''):
89 self.passwd = passwd
89 self.passwd = passwd
90
90
91 def gen_password(self, length, type_=None):
91 def gen_password(self, length, type_=None):
92 if type_ is None:
92 if type_ is None:
93 type_ = self.ALPHABETS_FULL
93 type_ = self.ALPHABETS_FULL
94 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
94 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
95 return self.passwd
95 return self.passwd
96
96
97
97
98 class KallitheaCrypto(object):
98 class KallitheaCrypto(object):
99
99
100 @classmethod
100 @classmethod
101 def hash_string(cls, str_):
101 def hash_string(cls, str_):
102 """
102 """
103 Cryptographic function used for password hashing based on pybcrypt
103 Cryptographic function used for password hashing based on pybcrypt
104 or pycrypto in windows
104 or pycrypto in windows
105
105
106 :param password: password to hash
106 :param password: password to hash
107 """
107 """
108 if is_windows:
108 if is_windows:
109 from hashlib import sha256
109 from hashlib import sha256
110 return sha256(str_).hexdigest()
110 return sha256(str_).hexdigest()
111 elif is_unix:
111 elif is_unix:
112 import bcrypt
112 import bcrypt
113 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
113 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
114 else:
114 else:
115 raise Exception('Unknown or unsupported platform %s' \
115 raise Exception('Unknown or unsupported platform %s' \
116 % __platform__)
116 % __platform__)
117
117
118 @classmethod
118 @classmethod
119 def hash_check(cls, password, hashed):
119 def hash_check(cls, password, hashed):
120 """
120 """
121 Checks matching password with it's hashed value, runs different
121 Checks matching password with it's hashed value, runs different
122 implementation based on platform it runs on
122 implementation based on platform it runs on
123
123
124 :param password: password
124 :param password: password
125 :param hashed: password in hashed form
125 :param hashed: password in hashed form
126 """
126 """
127
127
128 if is_windows:
128 if is_windows:
129 from hashlib import sha256
129 from hashlib import sha256
130 return sha256(password).hexdigest() == hashed
130 return sha256(password).hexdigest() == hashed
131 elif is_unix:
131 elif is_unix:
132 import bcrypt
132 import bcrypt
133 return bcrypt.hashpw(password, hashed) == hashed
133 return bcrypt.hashpw(password, hashed) == hashed
134 else:
134 else:
135 raise Exception('Unknown or unsupported platform %s' \
135 raise Exception('Unknown or unsupported platform %s' \
136 % __platform__)
136 % __platform__)
137
137
138
138
139 def get_crypt_password(password):
139 def get_crypt_password(password):
140 return KallitheaCrypto.hash_string(password)
140 return KallitheaCrypto.hash_string(password)
141
141
142
142
143 def check_password(password, hashed):
143 def check_password(password, hashed):
144 return KallitheaCrypto.hash_check(password, hashed)
144 return KallitheaCrypto.hash_check(password, hashed)
145
145
146
146
147 def generate_api_key(str_, salt=None):
147 def generate_api_key(str_, salt=None):
148 """
148 """
149 Generates API KEY from given string
149 Generates API KEY from given string
150
150
151 :param str_:
151 :param str_:
152 :param salt:
152 :param salt:
153 """
153 """
154
154
155 if salt is None:
155 if salt is None:
156 salt = _RandomNameSequence().next()
156 salt = _RandomNameSequence().next()
157
157
158 return hashlib.sha1(str_ + salt).hexdigest()
158 return hashlib.sha1(str_ + salt).hexdigest()
159
159
160
160
161 class CookieStoreWrapper(object):
161 class CookieStoreWrapper(object):
162
162
163 def __init__(self, cookie_store):
163 def __init__(self, cookie_store):
164 self.cookie_store = cookie_store
164 self.cookie_store = cookie_store
165
165
166 def __repr__(self):
166 def __repr__(self):
167 return 'CookieStore<%s>' % (self.cookie_store)
167 return 'CookieStore<%s>' % (self.cookie_store)
168
168
169 def get(self, key, other=None):
169 def get(self, key, other=None):
170 if isinstance(self.cookie_store, dict):
170 if isinstance(self.cookie_store, dict):
171 return self.cookie_store.get(key, other)
171 return self.cookie_store.get(key, other)
172 elif isinstance(self.cookie_store, AuthUser):
172 elif isinstance(self.cookie_store, AuthUser):
173 return self.cookie_store.__dict__.get(key, other)
173 return self.cookie_store.__dict__.get(key, other)
174
174
175
175
176
176
177 def _cached_perms_data(user_id, user_is_admin, user_inherit_default_permissions,
177 def _cached_perms_data(user_id, user_is_admin, user_inherit_default_permissions,
178 explicit, algo):
178 explicit, algo):
179 RK = 'repositories'
179 RK = 'repositories'
180 GK = 'repositories_groups'
180 GK = 'repositories_groups'
181 UK = 'user_groups'
181 UK = 'user_groups'
182 GLOBAL = 'global'
182 GLOBAL = 'global'
183 PERM_WEIGHTS = Permission.PERM_WEIGHTS
183 PERM_WEIGHTS = Permission.PERM_WEIGHTS
184 permissions = {RK: {}, GK: {}, UK: {}, GLOBAL: set()}
184 permissions = {RK: {}, GK: {}, UK: {}, GLOBAL: set()}
185
185
186 def _choose_perm(new_perm, cur_perm):
186 def _choose_perm(new_perm, cur_perm):
187 new_perm_val = PERM_WEIGHTS[new_perm]
187 new_perm_val = PERM_WEIGHTS[new_perm]
188 cur_perm_val = PERM_WEIGHTS[cur_perm]
188 cur_perm_val = PERM_WEIGHTS[cur_perm]
189 if algo == 'higherwin':
189 if algo == 'higherwin':
190 if new_perm_val > cur_perm_val:
190 if new_perm_val > cur_perm_val:
191 return new_perm
191 return new_perm
192 return cur_perm
192 return cur_perm
193 elif algo == 'lowerwin':
193 elif algo == 'lowerwin':
194 if new_perm_val < cur_perm_val:
194 if new_perm_val < cur_perm_val:
195 return new_perm
195 return new_perm
196 return cur_perm
196 return cur_perm
197
197
198 #======================================================================
198 #======================================================================
199 # fetch default permissions
199 # fetch default permissions
200 #======================================================================
200 #======================================================================
201 default_user = User.get_by_username('default', cache=True)
201 default_user = User.get_by_username('default', cache=True)
202 default_user_id = default_user.user_id
202 default_user_id = default_user.user_id
203
203
204 default_repo_perms = Permission.get_default_perms(default_user_id)
204 default_repo_perms = Permission.get_default_perms(default_user_id)
205 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
205 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
206 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
206 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
207
207
208 if user_is_admin:
208 if user_is_admin:
209 #==================================================================
209 #==================================================================
210 # admin user have all default rights for repositories
210 # admin user have all default rights for repositories
211 # and groups set to admin
211 # and groups set to admin
212 #==================================================================
212 #==================================================================
213 permissions[GLOBAL].add('hg.admin')
213 permissions[GLOBAL].add('hg.admin')
214 permissions[GLOBAL].add('hg.create.write_on_repogroup.true')
214 permissions[GLOBAL].add('hg.create.write_on_repogroup.true')
215
215
216 # repositories
216 # repositories
217 for perm in default_repo_perms:
217 for perm in default_repo_perms:
218 r_k = perm.UserRepoToPerm.repository.repo_name
218 r_k = perm.UserRepoToPerm.repository.repo_name
219 p = 'repository.admin'
219 p = 'repository.admin'
220 permissions[RK][r_k] = p
220 permissions[RK][r_k] = p
221
221
222 # repository groups
222 # repository groups
223 for perm in default_repo_groups_perms:
223 for perm in default_repo_groups_perms:
224 rg_k = perm.UserRepoGroupToPerm.group.group_name
224 rg_k = perm.UserRepoGroupToPerm.group.group_name
225 p = 'group.admin'
225 p = 'group.admin'
226 permissions[GK][rg_k] = p
226 permissions[GK][rg_k] = p
227
227
228 # user groups
228 # user groups
229 for perm in default_user_group_perms:
229 for perm in default_user_group_perms:
230 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
230 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
231 p = 'usergroup.admin'
231 p = 'usergroup.admin'
232 permissions[UK][u_k] = p
232 permissions[UK][u_k] = p
233 return permissions
233 return permissions
234
234
235 #==================================================================
235 #==================================================================
236 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
236 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
237 #==================================================================
237 #==================================================================
238 uid = user_id
238 uid = user_id
239
239
240 # default global permissions taken from the default user
240 # default global permissions taken from the default user
241 default_global_perms = UserToPerm.query()\
241 default_global_perms = UserToPerm.query()\
242 .filter(UserToPerm.user_id == default_user_id)\
242 .filter(UserToPerm.user_id == default_user_id)\
243 .options(joinedload(UserToPerm.permission))
243 .options(joinedload(UserToPerm.permission))
244
244
245 for perm in default_global_perms:
245 for perm in default_global_perms:
246 permissions[GLOBAL].add(perm.permission.permission_name)
246 permissions[GLOBAL].add(perm.permission.permission_name)
247
247
248 # defaults for repositories, taken from default user
248 # defaults for repositories, taken from default user
249 for perm in default_repo_perms:
249 for perm in default_repo_perms:
250 r_k = perm.UserRepoToPerm.repository.repo_name
250 r_k = perm.UserRepoToPerm.repository.repo_name
251 if perm.Repository.private and not (perm.Repository.user_id == uid):
251 if perm.Repository.private and not (perm.Repository.user_id == uid):
252 # disable defaults for private repos,
252 # disable defaults for private repos,
253 p = 'repository.none'
253 p = 'repository.none'
254 elif perm.Repository.user_id == uid:
254 elif perm.Repository.user_id == uid:
255 # set admin if owner
255 # set admin if owner
256 p = 'repository.admin'
256 p = 'repository.admin'
257 else:
257 else:
258 p = perm.Permission.permission_name
258 p = perm.Permission.permission_name
259
259
260 permissions[RK][r_k] = p
260 permissions[RK][r_k] = p
261
261
262 # defaults for repository groups taken from default user permission
262 # defaults for repository groups taken from default user permission
263 # on given group
263 # on given group
264 for perm in default_repo_groups_perms:
264 for perm in default_repo_groups_perms:
265 rg_k = perm.UserRepoGroupToPerm.group.group_name
265 rg_k = perm.UserRepoGroupToPerm.group.group_name
266 p = perm.Permission.permission_name
266 p = perm.Permission.permission_name
267 permissions[GK][rg_k] = p
267 permissions[GK][rg_k] = p
268
268
269 # defaults for user groups taken from default user permission
269 # defaults for user groups taken from default user permission
270 # on given user group
270 # on given user group
271 for perm in default_user_group_perms:
271 for perm in default_user_group_perms:
272 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
272 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
273 p = perm.Permission.permission_name
273 p = perm.Permission.permission_name
274 permissions[UK][u_k] = p
274 permissions[UK][u_k] = p
275
275
276 #======================================================================
276 #======================================================================
277 # !! OVERRIDE GLOBALS !! with user permissions if any found
277 # !! OVERRIDE GLOBALS !! with user permissions if any found
278 #======================================================================
278 #======================================================================
279 # those can be configured from groups or users explicitly
279 # those can be configured from groups or users explicitly
280 _configurable = set([
280 _configurable = set([
281 'hg.fork.none', 'hg.fork.repository',
281 'hg.fork.none', 'hg.fork.repository',
282 'hg.create.none', 'hg.create.repository',
282 'hg.create.none', 'hg.create.repository',
283 'hg.usergroup.create.false', 'hg.usergroup.create.true'
283 'hg.usergroup.create.false', 'hg.usergroup.create.true'
284 ])
284 ])
285
285
286 # USER GROUPS comes first
286 # USER GROUPS comes first
287 # user group global permissions
287 # user group global permissions
288 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
288 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
289 .options(joinedload(UserGroupToPerm.permission))\
289 .options(joinedload(UserGroupToPerm.permission))\
290 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
290 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
291 UserGroupMember.users_group_id))\
291 UserGroupMember.users_group_id))\
292 .filter(UserGroupMember.user_id == uid)\
292 .filter(UserGroupMember.user_id == uid)\
293 .order_by(UserGroupToPerm.users_group_id)\
293 .order_by(UserGroupToPerm.users_group_id)\
294 .all()
294 .all()
295 # need to group here by groups since user can be in more than
295 # need to group here by groups since user can be in more than
296 # one group
296 # one group
297 _grouped = [[x, list(y)] for x, y in
297 _grouped = [[x, list(y)] for x, y in
298 itertools.groupby(user_perms_from_users_groups,
298 itertools.groupby(user_perms_from_users_groups,
299 lambda x:x.users_group)]
299 lambda x:x.users_group)]
300 for gr, perms in _grouped:
300 for gr, perms in _grouped:
301 # since user can be in multiple groups iterate over them and
301 # since user can be in multiple groups iterate over them and
302 # select the lowest permissions first (more explicit)
302 # select the lowest permissions first (more explicit)
303 ##TODO: do this^^
303 ##TODO: do this^^
304 if not gr.inherit_default_permissions:
304 if not gr.inherit_default_permissions:
305 # NEED TO IGNORE all configurable permissions and
305 # NEED TO IGNORE all configurable permissions and
306 # replace them with explicitly set
306 # replace them with explicitly set
307 permissions[GLOBAL] = permissions[GLOBAL]\
307 permissions[GLOBAL] = permissions[GLOBAL]\
308 .difference(_configurable)
308 .difference(_configurable)
309 for perm in perms:
309 for perm in perms:
310 permissions[GLOBAL].add(perm.permission.permission_name)
310 permissions[GLOBAL].add(perm.permission.permission_name)
311
311
312 # user specific global permissions
312 # user specific global permissions
313 user_perms = Session().query(UserToPerm)\
313 user_perms = Session().query(UserToPerm)\
314 .options(joinedload(UserToPerm.permission))\
314 .options(joinedload(UserToPerm.permission))\
315 .filter(UserToPerm.user_id == uid).all()
315 .filter(UserToPerm.user_id == uid).all()
316
316
317 if not user_inherit_default_permissions:
317 if not user_inherit_default_permissions:
318 # NEED TO IGNORE all configurable permissions and
318 # NEED TO IGNORE all configurable permissions and
319 # replace them with explicitly set
319 # replace them with explicitly set
320 permissions[GLOBAL] = permissions[GLOBAL]\
320 permissions[GLOBAL] = permissions[GLOBAL]\
321 .difference(_configurable)
321 .difference(_configurable)
322
322
323 for perm in user_perms:
323 for perm in user_perms:
324 permissions[GLOBAL].add(perm.permission.permission_name)
324 permissions[GLOBAL].add(perm.permission.permission_name)
325 ## END GLOBAL PERMISSIONS
325 ## END GLOBAL PERMISSIONS
326
326
327 #======================================================================
327 #======================================================================
328 # !! PERMISSIONS FOR REPOSITORIES !!
328 # !! PERMISSIONS FOR REPOSITORIES !!
329 #======================================================================
329 #======================================================================
330 #======================================================================
330 #======================================================================
331 # check if user is part of user groups for this repository and
331 # check if user is part of user groups for this repository and
332 # fill in his permission from it. _choose_perm decides of which
332 # fill in his permission from it. _choose_perm decides of which
333 # permission should be selected based on selected method
333 # permission should be selected based on selected method
334 #======================================================================
334 #======================================================================
335
335
336 # user group for repositories permissions
336 # user group for repositories permissions
337 user_repo_perms_from_users_groups = \
337 user_repo_perms_from_users_groups = \
338 Session().query(UserGroupRepoToPerm, Permission, Repository,)\
338 Session().query(UserGroupRepoToPerm, Permission, Repository,)\
339 .join((Repository, UserGroupRepoToPerm.repository_id ==
339 .join((Repository, UserGroupRepoToPerm.repository_id ==
340 Repository.repo_id))\
340 Repository.repo_id))\
341 .join((Permission, UserGroupRepoToPerm.permission_id ==
341 .join((Permission, UserGroupRepoToPerm.permission_id ==
342 Permission.permission_id))\
342 Permission.permission_id))\
343 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
343 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
344 UserGroupMember.users_group_id))\
344 UserGroupMember.users_group_id))\
345 .filter(UserGroupMember.user_id == uid)\
345 .filter(UserGroupMember.user_id == uid)\
346 .all()
346 .all()
347
347
348 multiple_counter = collections.defaultdict(int)
348 multiple_counter = collections.defaultdict(int)
349 for perm in user_repo_perms_from_users_groups:
349 for perm in user_repo_perms_from_users_groups:
350 r_k = perm.UserGroupRepoToPerm.repository.repo_name
350 r_k = perm.UserGroupRepoToPerm.repository.repo_name
351 multiple_counter[r_k] += 1
351 multiple_counter[r_k] += 1
352 p = perm.Permission.permission_name
352 p = perm.Permission.permission_name
353 cur_perm = permissions[RK][r_k]
353 cur_perm = permissions[RK][r_k]
354
354
355 if perm.Repository.user_id == uid:
355 if perm.Repository.user_id == uid:
356 # set admin if owner
356 # set admin if owner
357 p = 'repository.admin'
357 p = 'repository.admin'
358 else:
358 else:
359 if multiple_counter[r_k] > 1:
359 if multiple_counter[r_k] > 1:
360 p = _choose_perm(p, cur_perm)
360 p = _choose_perm(p, cur_perm)
361 permissions[RK][r_k] = p
361 permissions[RK][r_k] = p
362
362
363 # user explicit permissions for repositories, overrides any specified
363 # user explicit permissions for repositories, overrides any specified
364 # by the group permission
364 # by the group permission
365 user_repo_perms = Permission.get_default_perms(uid)
365 user_repo_perms = Permission.get_default_perms(uid)
366 for perm in user_repo_perms:
366 for perm in user_repo_perms:
367 r_k = perm.UserRepoToPerm.repository.repo_name
367 r_k = perm.UserRepoToPerm.repository.repo_name
368 cur_perm = permissions[RK][r_k]
368 cur_perm = permissions[RK][r_k]
369 # set admin if owner
369 # set admin if owner
370 if perm.Repository.user_id == uid:
370 if perm.Repository.user_id == uid:
371 p = 'repository.admin'
371 p = 'repository.admin'
372 else:
372 else:
373 p = perm.Permission.permission_name
373 p = perm.Permission.permission_name
374 if not explicit:
374 if not explicit:
375 p = _choose_perm(p, cur_perm)
375 p = _choose_perm(p, cur_perm)
376 permissions[RK][r_k] = p
376 permissions[RK][r_k] = p
377
377
378 #======================================================================
378 #======================================================================
379 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
379 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
380 #======================================================================
380 #======================================================================
381 #======================================================================
381 #======================================================================
382 # check if user is part of user groups for this repository groups and
382 # check if user is part of user groups for this repository groups and
383 # fill in his permission from it. _choose_perm decides of which
383 # fill in his permission from it. _choose_perm decides of which
384 # permission should be selected based on selected method
384 # permission should be selected based on selected method
385 #======================================================================
385 #======================================================================
386 # user group for repo groups permissions
386 # user group for repo groups permissions
387 user_repo_group_perms_from_users_groups = \
387 user_repo_group_perms_from_users_groups = \
388 Session().query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
388 Session().query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
389 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
389 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
390 .join((Permission, UserGroupRepoGroupToPerm.permission_id
390 .join((Permission, UserGroupRepoGroupToPerm.permission_id
391 == Permission.permission_id))\
391 == Permission.permission_id))\
392 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
392 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
393 == UserGroupMember.users_group_id))\
393 == UserGroupMember.users_group_id))\
394 .filter(UserGroupMember.user_id == uid)\
394 .filter(UserGroupMember.user_id == uid)\
395 .all()
395 .all()
396
396
397 multiple_counter = collections.defaultdict(int)
397 multiple_counter = collections.defaultdict(int)
398 for perm in user_repo_group_perms_from_users_groups:
398 for perm in user_repo_group_perms_from_users_groups:
399 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
399 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
400 multiple_counter[g_k] += 1
400 multiple_counter[g_k] += 1
401 p = perm.Permission.permission_name
401 p = perm.Permission.permission_name
402 cur_perm = permissions[GK][g_k]
402 cur_perm = permissions[GK][g_k]
403 if multiple_counter[g_k] > 1:
403 if multiple_counter[g_k] > 1:
404 p = _choose_perm(p, cur_perm)
404 p = _choose_perm(p, cur_perm)
405 permissions[GK][g_k] = p
405 permissions[GK][g_k] = p
406
406
407 # user explicit permissions for repository groups
407 # user explicit permissions for repository groups
408 user_repo_groups_perms = Permission.get_default_group_perms(uid)
408 user_repo_groups_perms = Permission.get_default_group_perms(uid)
409 for perm in user_repo_groups_perms:
409 for perm in user_repo_groups_perms:
410 rg_k = perm.UserRepoGroupToPerm.group.group_name
410 rg_k = perm.UserRepoGroupToPerm.group.group_name
411 p = perm.Permission.permission_name
411 p = perm.Permission.permission_name
412 cur_perm = permissions[GK][rg_k]
412 cur_perm = permissions[GK][rg_k]
413 if not explicit:
413 if not explicit:
414 p = _choose_perm(p, cur_perm)
414 p = _choose_perm(p, cur_perm)
415 permissions[GK][rg_k] = p
415 permissions[GK][rg_k] = p
416
416
417 #======================================================================
417 #======================================================================
418 # !! PERMISSIONS FOR USER GROUPS !!
418 # !! PERMISSIONS FOR USER GROUPS !!
419 #======================================================================
419 #======================================================================
420 # user group for user group permissions
420 # user group for user group permissions
421 user_group_user_groups_perms = \
421 user_group_user_groups_perms = \
422 Session().query(UserGroupUserGroupToPerm, Permission, UserGroup)\
422 Session().query(UserGroupUserGroupToPerm, Permission, UserGroup)\
423 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
423 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
424 == UserGroup.users_group_id))\
424 == UserGroup.users_group_id))\
425 .join((Permission, UserGroupUserGroupToPerm.permission_id
425 .join((Permission, UserGroupUserGroupToPerm.permission_id
426 == Permission.permission_id))\
426 == Permission.permission_id))\
427 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
427 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
428 == UserGroupMember.users_group_id))\
428 == UserGroupMember.users_group_id))\
429 .filter(UserGroupMember.user_id == uid)\
429 .filter(UserGroupMember.user_id == uid)\
430 .all()
430 .all()
431
431
432 multiple_counter = collections.defaultdict(int)
432 multiple_counter = collections.defaultdict(int)
433 for perm in user_group_user_groups_perms:
433 for perm in user_group_user_groups_perms:
434 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
434 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
435 multiple_counter[g_k] += 1
435 multiple_counter[g_k] += 1
436 p = perm.Permission.permission_name
436 p = perm.Permission.permission_name
437 cur_perm = permissions[UK][g_k]
437 cur_perm = permissions[UK][g_k]
438 if multiple_counter[g_k] > 1:
438 if multiple_counter[g_k] > 1:
439 p = _choose_perm(p, cur_perm)
439 p = _choose_perm(p, cur_perm)
440 permissions[UK][g_k] = p
440 permissions[UK][g_k] = p
441
441
442 #user explicit permission for user groups
442 #user explicit permission for user groups
443 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
443 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
444 for perm in user_user_groups_perms:
444 for perm in user_user_groups_perms:
445 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
445 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
446 p = perm.Permission.permission_name
446 p = perm.Permission.permission_name
447 cur_perm = permissions[UK][u_k]
447 cur_perm = permissions[UK][u_k]
448 if not explicit:
448 if not explicit:
449 p = _choose_perm(p, cur_perm)
449 p = _choose_perm(p, cur_perm)
450 permissions[UK][u_k] = p
450 permissions[UK][u_k] = p
451
451
452 return permissions
452 return permissions
453
453
454
454
455 def allowed_api_access(controller_name, whitelist=None, api_key=None):
455 def allowed_api_access(controller_name, whitelist=None, api_key=None):
456 """
456 """
457 Check if given controller_name is in whitelist API access
457 Check if given controller_name is in whitelist API access
458 """
458 """
459 if not whitelist:
459 if not whitelist:
460 from kallithea import CONFIG
460 from kallithea import CONFIG
461 whitelist = aslist(CONFIG.get('api_access_controllers_whitelist'),
461 whitelist = aslist(CONFIG.get('api_access_controllers_whitelist'),
462 sep=',')
462 sep=',')
463 log.debug('whitelist of API access is: %s' % (whitelist))
463 log.debug('whitelist of API access is: %s' % (whitelist))
464 api_access_valid = controller_name in whitelist
464 api_access_valid = controller_name in whitelist
465 if api_access_valid:
465 if api_access_valid:
466 log.debug('controller:%s is in API whitelist' % (controller_name))
466 log.debug('controller:%s is in API whitelist' % (controller_name))
467 else:
467 else:
468 msg = 'controller: %s is *NOT* in API whitelist' % (controller_name)
468 msg = 'controller: %s is *NOT* in API whitelist' % (controller_name)
469 if api_key:
469 if api_key:
470 #if we use API key and don't have access it's a warning
470 #if we use API key and don't have access it's a warning
471 log.warning(msg)
471 log.warning(msg)
472 else:
472 else:
473 log.debug(msg)
473 log.debug(msg)
474 return api_access_valid
474 return api_access_valid
475
475
476
476
477 class AuthUser(object):
477 class AuthUser(object):
478 """
478 """
479 A simple object that handles all attributes of user in Kallithea
479 A simple object that handles all attributes of user in Kallithea
480
480
481 It does lookup based on API key,given user, or user present in session
481 It does lookup based on API key,given user, or user present in session
482 Then it fills all required information for such user. It also checks if
482 Then it fills all required information for such user. It also checks if
483 anonymous access is enabled and if so, it returns default user as logged in
483 anonymous access is enabled and if so, it returns default user as logged in
484 """
484 """
485
485
486 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
486 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
487
487
488 self.user_id = user_id
488 self.user_id = user_id
489 self._api_key = api_key
489 self._api_key = api_key
490
490
491 self.api_key = None
491 self.api_key = None
492 self.username = username
492 self.username = username
493 self.ip_addr = ip_addr
493 self.ip_addr = ip_addr
494 self.name = ''
494 self.name = ''
495 self.lastname = ''
495 self.lastname = ''
496 self.email = ''
496 self.email = ''
497 self.is_authenticated = False
497 self.is_authenticated = False
498 self.admin = False
498 self.admin = False
499 self.inherit_default_permissions = False
499 self.inherit_default_permissions = False
500
500
501 self.propagate_data()
501 self.propagate_data()
502 self._instance = None
502 self._instance = None
503
503
504 @LazyProperty
504 @LazyProperty
505 def permissions(self):
505 def permissions(self):
506 return self.get_perms(user=self, cache=False)
506 return self.get_perms(user=self, cache=False)
507
507
508 @property
508 @property
509 def api_keys(self):
509 def api_keys(self):
510 return self.get_api_keys()
510 return self.get_api_keys()
511
511
512 def propagate_data(self):
512 def propagate_data(self):
513 user_model = UserModel()
513 user_model = UserModel()
514 self.anonymous_user = User.get_default_user(cache=True)
514 self.anonymous_user = User.get_default_user(cache=True)
515 is_user_loaded = False
515 is_user_loaded = False
516
516
517 # lookup by userid
517 # lookup by userid
518 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
518 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
519 log.debug('Auth User lookup by USER ID %s' % self.user_id)
519 log.debug('Auth User lookup by USER ID %s' % self.user_id)
520 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
520 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
521
521
522 # try go get user by api key
522 # try go get user by api key
523 elif self._api_key and self._api_key != self.anonymous_user.api_key:
523 elif self._api_key and self._api_key != self.anonymous_user.api_key:
524 log.debug('Auth User lookup by API KEY %s' % self._api_key)
524 log.debug('Auth User lookup by API KEY %s' % self._api_key)
525 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
525 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
526
526
527 # lookup by username
527 # lookup by username
528 elif self.username:
528 elif self.username:
529 log.debug('Auth User lookup by USER NAME %s' % self.username)
529 log.debug('Auth User lookup by USER NAME %s' % self.username)
530 is_user_loaded = user_model.fill_data(self, username=self.username)
530 is_user_loaded = user_model.fill_data(self, username=self.username)
531 else:
531 else:
532 log.debug('No data in %s that could been used to log in' % self)
532 log.debug('No data in %s that could been used to log in' % self)
533
533
534 if not is_user_loaded:
534 if not is_user_loaded:
535 # if we cannot authenticate user try anonymous
535 # if we cannot authenticate user try anonymous
536 if self.anonymous_user.active:
536 if self.anonymous_user.active:
537 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
537 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
538 # then we set this user is logged in
538 # then we set this user is logged in
539 self.is_authenticated = True
539 self.is_authenticated = True
540 else:
540 else:
541 self.user_id = None
541 self.user_id = None
542 self.username = None
542 self.username = None
543 self.is_authenticated = False
543 self.is_authenticated = False
544
544
545 if not self.username:
545 if not self.username:
546 self.username = 'None'
546 self.username = 'None'
547
547
548 log.debug('Auth User is now %s' % self)
548 log.debug('Auth User is now %s' % self)
549
549
550 def get_perms(self, user, explicit=True, algo='higherwin', cache=False):
550 def get_perms(self, user, explicit=True, algo='higherwin', cache=False):
551 """
551 """
552 Fills user permission attribute with permissions taken from database
552 Fills user permission attribute with permissions taken from database
553 works for permissions given for repositories, and for permissions that
553 works for permissions given for repositories, and for permissions that
554 are granted to groups
554 are granted to groups
555
555
556 :param user: instance of User object from database
556 :param user: instance of User object from database
557 :param explicit: In case there are permissions both for user and a group
557 :param explicit: In case there are permissions both for user and a group
558 that user is part of, explicit flag will define if user will
558 that user is part of, explicit flag will define if user will
559 explicitly override permissions from group, if it's False it will
559 explicitly override permissions from group, if it's False it will
560 make decision based on the algo
560 make decision based on the algo
561 :param algo: algorithm to decide what permission should be choose if
561 :param algo: algorithm to decide what permission should be choose if
562 it's multiple defined, eg user in two different groups. It also
562 it's multiple defined, eg user in two different groups. It also
563 decides if explicit flag is turned off how to specify the permission
563 decides if explicit flag is turned off how to specify the permission
564 for case when user is in a group + have defined separate permission
564 for case when user is in a group + have defined separate permission
565 """
565 """
566 user_id = user.user_id
566 user_id = user.user_id
567 user_is_admin = user.is_admin
567 user_is_admin = user.is_admin
568 user_inherit_default_permissions = user.inherit_default_permissions
568 user_inherit_default_permissions = user.inherit_default_permissions
569
569
570 log.debug('Getting PERMISSION tree')
570 log.debug('Getting PERMISSION tree')
571 compute = conditional_cache('short_term', 'cache_desc',
571 compute = conditional_cache('short_term', 'cache_desc',
572 condition=cache, func=_cached_perms_data)
572 condition=cache, func=_cached_perms_data)
573 return compute(user_id, user_is_admin,
573 return compute(user_id, user_is_admin,
574 user_inherit_default_permissions, explicit, algo)
574 user_inherit_default_permissions, explicit, algo)
575
575
576 def get_api_keys(self):
576 def get_api_keys(self):
577 api_keys = [self.api_key]
577 api_keys = [self.api_key]
578 for api_key in UserApiKeys.query()\
578 for api_key in UserApiKeys.query()\
579 .filter(UserApiKeys.user_id == self.user_id)\
579 .filter(UserApiKeys.user_id == self.user_id)\
580 .filter(or_(UserApiKeys.expires == -1,
580 .filter(or_(UserApiKeys.expires == -1,
581 UserApiKeys.expires >= time.time())).all():
581 UserApiKeys.expires >= time.time())).all():
582 api_keys.append(api_key.api_key)
582 api_keys.append(api_key.api_key)
583
583
584 return api_keys
584 return api_keys
585
585
586 @property
586 @property
587 def is_admin(self):
587 def is_admin(self):
588 return self.admin
588 return self.admin
589
589
590 @property
590 @property
591 def repositories_admin(self):
591 def repositories_admin(self):
592 """
592 """
593 Returns list of repositories you're an admin of
593 Returns list of repositories you're an admin of
594 """
594 """
595 return [x[0] for x in self.permissions['repositories'].iteritems()
595 return [x[0] for x in self.permissions['repositories'].iteritems()
596 if x[1] == 'repository.admin']
596 if x[1] == 'repository.admin']
597
597
598 @property
598 @property
599 def repository_groups_admin(self):
599 def repository_groups_admin(self):
600 """
600 """
601 Returns list of repository groups you're an admin of
601 Returns list of repository groups you're an admin of
602 """
602 """
603 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
603 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
604 if x[1] == 'group.admin']
604 if x[1] == 'group.admin']
605
605
606 @property
606 @property
607 def user_groups_admin(self):
607 def user_groups_admin(self):
608 """
608 """
609 Returns list of user groups you're an admin of
609 Returns list of user groups you're an admin of
610 """
610 """
611 return [x[0] for x in self.permissions['user_groups'].iteritems()
611 return [x[0] for x in self.permissions['user_groups'].iteritems()
612 if x[1] == 'usergroup.admin']
612 if x[1] == 'usergroup.admin']
613
613
614 @property
614 @property
615 def ip_allowed(self):
615 def ip_allowed(self):
616 """
616 """
617 Checks if ip_addr used in constructor is allowed from defined list of
617 Checks if ip_addr used in constructor is allowed from defined list of
618 allowed ip_addresses for user
618 allowed ip_addresses for user
619
619
620 :returns: boolean, True if ip is in allowed ip range
620 :returns: boolean, True if ip is in allowed ip range
621 """
621 """
622 # check IP
622 # check IP
623 inherit = self.inherit_default_permissions
623 inherit = self.inherit_default_permissions
624 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
624 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
625 inherit_from_default=inherit)
625 inherit_from_default=inherit)
626
626
627 @classmethod
627 @classmethod
628 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
628 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
629 allowed_ips = AuthUser.get_allowed_ips(user_id, cache=True,
629 allowed_ips = AuthUser.get_allowed_ips(user_id, cache=True,
630 inherit_from_default=inherit_from_default)
630 inherit_from_default=inherit_from_default)
631 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
631 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
632 log.debug('IP:%s is in range of %s' % (ip_addr, allowed_ips))
632 log.debug('IP:%s is in range of %s' % (ip_addr, allowed_ips))
633 return True
633 return True
634 else:
634 else:
635 log.info('Access for IP:%s forbidden, '
635 log.info('Access for IP:%s forbidden, '
636 'not in %s' % (ip_addr, allowed_ips))
636 'not in %s' % (ip_addr, allowed_ips))
637 return False
637 return False
638
638
639 def __repr__(self):
639 def __repr__(self):
640 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
640 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
641 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
641 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
642
642
643 def set_authenticated(self, authenticated=True):
643 def set_authenticated(self, authenticated=True):
644 if self.user_id != self.anonymous_user.user_id:
644 if self.user_id != self.anonymous_user.user_id:
645 self.is_authenticated = authenticated
645 self.is_authenticated = authenticated
646
646
647 def get_cookie_store(self):
647 def get_cookie_store(self):
648 return {'username': self.username,
648 return {'username': self.username,
649 'user_id': self.user_id,
649 'user_id': self.user_id,
650 'is_authenticated': self.is_authenticated}
650 'is_authenticated': self.is_authenticated}
651
651
652 @classmethod
652 @classmethod
653 def from_cookie_store(cls, cookie_store):
653 def from_cookie_store(cls, cookie_store):
654 """
654 """
655 Creates AuthUser from a cookie store
655 Creates AuthUser from a cookie store
656
656
657 :param cls:
657 :param cls:
658 :param cookie_store:
658 :param cookie_store:
659 """
659 """
660 user_id = cookie_store.get('user_id')
660 user_id = cookie_store.get('user_id')
661 username = cookie_store.get('username')
661 username = cookie_store.get('username')
662 api_key = cookie_store.get('api_key')
662 api_key = cookie_store.get('api_key')
663 return AuthUser(user_id, api_key, username)
663 return AuthUser(user_id, api_key, username)
664
664
665 @classmethod
665 @classmethod
666 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
666 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
667 _set = set()
667 _set = set()
668
668
669 if inherit_from_default:
669 if inherit_from_default:
670 default_ips = UserIpMap.query().filter(UserIpMap.user ==
670 default_ips = UserIpMap.query().filter(UserIpMap.user ==
671 User.get_default_user(cache=True))
671 User.get_default_user(cache=True))
672 if cache:
672 if cache:
673 default_ips = default_ips.options(FromCache("sql_cache_short",
673 default_ips = default_ips.options(FromCache("sql_cache_short",
674 "get_user_ips_default"))
674 "get_user_ips_default"))
675
675
676 # populate from default user
676 # populate from default user
677 for ip in default_ips:
677 for ip in default_ips:
678 try:
678 try:
679 _set.add(ip.ip_addr)
679 _set.add(ip.ip_addr)
680 except ObjectDeletedError:
680 except ObjectDeletedError:
681 # since we use heavy caching sometimes it happens that we get
681 # since we use heavy caching sometimes it happens that we get
682 # deleted objects here, we just skip them
682 # deleted objects here, we just skip them
683 pass
683 pass
684
684
685 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
685 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
686 if cache:
686 if cache:
687 user_ips = user_ips.options(FromCache("sql_cache_short",
687 user_ips = user_ips.options(FromCache("sql_cache_short",
688 "get_user_ips_%s" % user_id))
688 "get_user_ips_%s" % user_id))
689
689
690 for ip in user_ips:
690 for ip in user_ips:
691 try:
691 try:
692 _set.add(ip.ip_addr)
692 _set.add(ip.ip_addr)
693 except ObjectDeletedError:
693 except ObjectDeletedError:
694 # since we use heavy caching sometimes it happens that we get
694 # since we use heavy caching sometimes it happens that we get
695 # deleted objects here, we just skip them
695 # deleted objects here, we just skip them
696 pass
696 pass
697 return _set or set(['0.0.0.0/0', '::/0'])
697 return _set or set(['0.0.0.0/0', '::/0'])
698
698
699
699
700 def set_available_permissions(config):
700 def set_available_permissions(config):
701 """
701 """
702 This function will propagate pylons globals with all available defined
702 This function will propagate pylons globals with all available defined
703 permission given in db. We don't want to check each time from db for new
703 permission given in db. We don't want to check each time from db for new
704 permissions since adding a new permission also requires application restart
704 permissions since adding a new permission also requires application restart
705 ie. to decorate new views with the newly created permission
705 ie. to decorate new views with the newly created permission
706
706
707 :param config: current pylons config instance
707 :param config: current pylons config instance
708
708
709 """
709 """
710 log.info('getting information about all available permissions')
710 log.info('getting information about all available permissions')
711 try:
711 try:
712 sa = meta.Session
712 sa = meta.Session
713 all_perms = sa.query(Permission).all()
713 all_perms = sa.query(Permission).all()
714 config['available_permissions'] = [x.permission_name for x in all_perms]
714 config['available_permissions'] = [x.permission_name for x in all_perms]
715 finally:
715 finally:
716 meta.Session.remove()
716 meta.Session.remove()
717
717
718
718
719 #==============================================================================
719 #==============================================================================
720 # CHECK DECORATORS
720 # CHECK DECORATORS
721 #==============================================================================
721 #==============================================================================
722
723 def redirect_to_login(message=None):
724 from kallithea.lib import helpers as h
725 p = url.current()
726 h.flash(h.literal(message), category='warning')
727 log.debug('Redirecting to login page, origin: %s' % p)
728 return redirect(url('login_home', came_from=p))
729
722 class LoginRequired(object):
730 class LoginRequired(object):
723 """
731 """
724 Must be logged in to execute this function else
732 Must be logged in to execute this function else
725 redirect to login page
733 redirect to login page
726
734
727 :param api_access: if enabled this checks only for valid auth token
735 :param api_access: if enabled this checks only for valid auth token
728 and grants access based on valid token
736 and grants access based on valid token
729 """
737 """
730
738
731 def __init__(self, api_access=False):
739 def __init__(self, api_access=False):
732 self.api_access = api_access
740 self.api_access = api_access
733
741
734 def __call__(self, func):
742 def __call__(self, func):
735 return decorator(self.__wrapper, func)
743 return decorator(self.__wrapper, func)
736
744
737 def __wrapper(self, func, *fargs, **fkwargs):
745 def __wrapper(self, func, *fargs, **fkwargs):
738 cls = fargs[0]
746 cls = fargs[0]
739 user = cls.authuser
747 user = cls.authuser
740 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
748 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
749 log.debug('Checking access for user %s @ %s' % (user, loc))
741
750
742 # check if our IP is allowed
751 # check if our IP is allowed
743 ip_access_valid = True
744 if not user.ip_allowed:
752 if not user.ip_allowed:
745 from kallithea.lib import helpers as h
753 return redirect_to_login(_('IP %s not allowed' % (user.ip_addr)))
746 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
747 category='warning')
748 ip_access_valid = False
749
754
750 # check if we used an APIKEY and it's a valid one
755 # check if we used an APIKEY and it's a valid one
751 # defined whitelist of controllers which API access will be enabled
756 # defined whitelist of controllers which API access will be enabled
752 _api_key = request.GET.get('api_key', '')
757 _api_key = request.GET.get('api_key', '')
753 api_access_valid = allowed_api_access(loc, api_key=_api_key)
758 api_access_valid = allowed_api_access(loc, api_key=_api_key)
754
759
755 # explicit controller is enabled or API is in our whitelist
760 # explicit controller is enabled or API is in our whitelist
756 if self.api_access or api_access_valid:
761 if self.api_access or api_access_valid:
757 log.debug('Checking API KEY access for %s' % cls)
762 log.debug('Checking API KEY access for %s' % cls)
758 if _api_key and _api_key in user.api_keys:
763 if _api_key and _api_key in user.api_keys:
759 api_access_valid = True
764 api_access_valid = True
760 log.debug('API KEY ****%s is VALID' % _api_key[-4:])
765 log.debug('API KEY ****%s is VALID' % _api_key[-4:])
761 else:
766 else:
762 api_access_valid = False
767 api_access_valid = False
763 if not _api_key:
768 if not _api_key:
764 log.debug("API KEY *NOT* present in request")
769 log.debug("API KEY *NOT* present in request")
765 else:
770 else:
766 log.warning("API KEY ****%s *NOT* valid" % _api_key[-4:])
771 log.warning("API KEY ****%s *NOT* valid" % _api_key[-4:])
767
772
768 # CSRF protection - POSTs with session auth must contain correct token
773 # CSRF protection - POSTs with session auth must contain correct token
769 if request.POST and user.is_authenticated and not api_access_valid:
774 if request.POST and user.is_authenticated and not api_access_valid:
770 token = request.POST.get(secure_form.token_key)
775 token = request.POST.get(secure_form.token_key)
771 if not token or token != secure_form.authentication_token():
776 if not token or token != secure_form.authentication_token():
772 log.error('CSRF check failed')
777 log.error('CSRF check failed')
773 return abort(403)
778 return abort(403)
774
779
775 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
780 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
776 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
781 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
777
782
778 if ip_access_valid and (user.is_authenticated or api_access_valid):
783 if user.is_authenticated or api_access_valid:
779 log.info('user %s authenticating with:%s IS authenticated on func %s '
784 log.info('user %s authenticating with:%s IS authenticated on func %s '
780 % (user, reason, loc)
785 % (user, reason, loc)
781 )
786 )
782 return func(*fargs, **fkwargs)
787 return func(*fargs, **fkwargs)
783 else:
788 else:
784 log.warning('user %s authenticating with:%s NOT authenticated on func: %s: '
789 log.warning('user %s authenticating with:%s NOT authenticated on func: %s: '
785 'IP_ACCESS:%s API_ACCESS:%s'
790 'API_ACCESS:%s'
786 % (user, reason, loc, ip_access_valid, api_access_valid)
791 % (user, reason, loc, api_access_valid)
787 )
792 )
788 p = url.current()
793 return redirect_to_login()
789
790 log.debug('redirecting to login page with %s' % p)
791 return redirect(url('login_home', came_from=p))
792
793
794
794 class NotAnonymous(object):
795 class NotAnonymous(object):
795 """
796 """
796 Must be logged in to execute this function else
797 Must be logged in to execute this function else
797 redirect to login page"""
798 redirect to login page"""
798
799
799 def __call__(self, func):
800 def __call__(self, func):
800 return decorator(self.__wrapper, func)
801 return decorator(self.__wrapper, func)
801
802
802 def __wrapper(self, func, *fargs, **fkwargs):
803 def __wrapper(self, func, *fargs, **fkwargs):
803 cls = fargs[0]
804 cls = fargs[0]
804 self.user = cls.authuser
805 self.user = cls.authuser
805
806
806 log.debug('Checking if user is not anonymous @%s' % cls)
807 log.debug('Checking if user is not anonymous @%s' % cls)
807
808
808 anonymous = self.user.username == User.DEFAULT_USER
809 anonymous = self.user.username == User.DEFAULT_USER
809
810
810 if anonymous:
811 if anonymous:
811 p = url.current()
812 return redirect_to_login(_('You need to be a registered user to '
812
813 'perform this action'))
813 import kallithea.lib.helpers as h
814 h.flash(_('You need to be a registered user to '
815 'perform this action'),
816 category='warning')
817 return redirect(url('login_home', came_from=p))
818 else:
814 else:
819 return func(*fargs, **fkwargs)
815 return func(*fargs, **fkwargs)
820
816
821
817
822 class PermsDecorator(object):
818 class PermsDecorator(object):
823 """Base class for controller decorators"""
819 """Base class for controller decorators"""
824
820
825 def __init__(self, *required_perms):
821 def __init__(self, *required_perms):
826 self.required_perms = set(required_perms)
822 self.required_perms = set(required_perms)
827 self.user_perms = None
823 self.user_perms = None
828
824
829 def __call__(self, func):
825 def __call__(self, func):
830 return decorator(self.__wrapper, func)
826 return decorator(self.__wrapper, func)
831
827
832 def __wrapper(self, func, *fargs, **fkwargs):
828 def __wrapper(self, func, *fargs, **fkwargs):
833 cls = fargs[0]
829 cls = fargs[0]
834 self.user = cls.authuser
830 self.user = cls.authuser
835 self.user_perms = self.user.permissions
831 self.user_perms = self.user.permissions
836 log.debug('checking %s permissions %s for %s %s',
832 log.debug('checking %s permissions %s for %s %s',
837 self.__class__.__name__, self.required_perms, cls, self.user)
833 self.__class__.__name__, self.required_perms, cls, self.user)
838
834
839 if self.check_permissions():
835 if self.check_permissions():
840 log.debug('Permission granted for %s %s' % (cls, self.user))
836 log.debug('Permission granted for %s %s' % (cls, self.user))
841 return func(*fargs, **fkwargs)
837 return func(*fargs, **fkwargs)
842
838
843 else:
839 else:
844 log.debug('Permission denied for %s %s' % (cls, self.user))
840 log.debug('Permission denied for %s %s' % (cls, self.user))
845 anonymous = self.user.username == User.DEFAULT_USER
841 anonymous = self.user.username == User.DEFAULT_USER
846
842
847 if anonymous:
843 if anonymous:
848 p = url.current()
844 return redirect_to_login(_('You need to be signed in to view this page'))
849
850 import kallithea.lib.helpers as h
851 h.flash(_('You need to be signed in to '
852 'view this page'),
853 category='warning')
854 return redirect(url('login_home', came_from=p))
855
856 else:
845 else:
857 # redirect with forbidden ret code
846 # redirect with forbidden ret code
858 return abort(403)
847 return abort(403)
859
848
860 def check_permissions(self):
849 def check_permissions(self):
861 """Dummy function for overriding"""
850 """Dummy function for overriding"""
862 raise Exception('You have to write this function in child class')
851 raise Exception('You have to write this function in child class')
863
852
864
853
865 class HasPermissionAllDecorator(PermsDecorator):
854 class HasPermissionAllDecorator(PermsDecorator):
866 """
855 """
867 Checks for access permission for all given predicates. All of them
856 Checks for access permission for all given predicates. All of them
868 have to be meet in order to fulfill the request
857 have to be meet in order to fulfill the request
869 """
858 """
870
859
871 def check_permissions(self):
860 def check_permissions(self):
872 if self.required_perms.issubset(self.user_perms.get('global')):
861 if self.required_perms.issubset(self.user_perms.get('global')):
873 return True
862 return True
874 return False
863 return False
875
864
876
865
877 class HasPermissionAnyDecorator(PermsDecorator):
866 class HasPermissionAnyDecorator(PermsDecorator):
878 """
867 """
879 Checks for access permission for any of given predicates. In order to
868 Checks for access permission for any of given predicates. In order to
880 fulfill the request any of predicates must be meet
869 fulfill the request any of predicates must be meet
881 """
870 """
882
871
883 def check_permissions(self):
872 def check_permissions(self):
884 if self.required_perms.intersection(self.user_perms.get('global')):
873 if self.required_perms.intersection(self.user_perms.get('global')):
885 return True
874 return True
886 return False
875 return False
887
876
888
877
889 class HasRepoPermissionAllDecorator(PermsDecorator):
878 class HasRepoPermissionAllDecorator(PermsDecorator):
890 """
879 """
891 Checks for access permission for all given predicates for specific
880 Checks for access permission for all given predicates for specific
892 repository. All of them have to be meet in order to fulfill the request
881 repository. All of them have to be meet in order to fulfill the request
893 """
882 """
894
883
895 def check_permissions(self):
884 def check_permissions(self):
896 repo_name = get_repo_slug(request)
885 repo_name = get_repo_slug(request)
897 try:
886 try:
898 user_perms = set([self.user_perms['repositories'][repo_name]])
887 user_perms = set([self.user_perms['repositories'][repo_name]])
899 except KeyError:
888 except KeyError:
900 return False
889 return False
901 if self.required_perms.issubset(user_perms):
890 if self.required_perms.issubset(user_perms):
902 return True
891 return True
903 return False
892 return False
904
893
905
894
906 class HasRepoPermissionAnyDecorator(PermsDecorator):
895 class HasRepoPermissionAnyDecorator(PermsDecorator):
907 """
896 """
908 Checks for access permission for any of given predicates for specific
897 Checks for access permission for any of given predicates for specific
909 repository. In order to fulfill the request any of predicates must be meet
898 repository. In order to fulfill the request any of predicates must be meet
910 """
899 """
911
900
912 def check_permissions(self):
901 def check_permissions(self):
913 repo_name = get_repo_slug(request)
902 repo_name = get_repo_slug(request)
914 try:
903 try:
915 user_perms = set([self.user_perms['repositories'][repo_name]])
904 user_perms = set([self.user_perms['repositories'][repo_name]])
916 except KeyError:
905 except KeyError:
917 return False
906 return False
918
907
919 if self.required_perms.intersection(user_perms):
908 if self.required_perms.intersection(user_perms):
920 return True
909 return True
921 return False
910 return False
922
911
923
912
924 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
913 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
925 """
914 """
926 Checks for access permission for all given predicates for specific
915 Checks for access permission for all given predicates for specific
927 repository group. All of them have to be meet in order to fulfill the request
916 repository group. All of them have to be meet in order to fulfill the request
928 """
917 """
929
918
930 def check_permissions(self):
919 def check_permissions(self):
931 group_name = get_repo_group_slug(request)
920 group_name = get_repo_group_slug(request)
932 try:
921 try:
933 user_perms = set([self.user_perms['repositories_groups'][group_name]])
922 user_perms = set([self.user_perms['repositories_groups'][group_name]])
934 except KeyError:
923 except KeyError:
935 return False
924 return False
936
925
937 if self.required_perms.issubset(user_perms):
926 if self.required_perms.issubset(user_perms):
938 return True
927 return True
939 return False
928 return False
940
929
941
930
942 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
931 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
943 """
932 """
944 Checks for access permission for any of given predicates for specific
933 Checks for access permission for any of given predicates for specific
945 repository group. In order to fulfill the request any of predicates must be meet
934 repository group. In order to fulfill the request any of predicates must be meet
946 """
935 """
947
936
948 def check_permissions(self):
937 def check_permissions(self):
949 group_name = get_repo_group_slug(request)
938 group_name = get_repo_group_slug(request)
950 try:
939 try:
951 user_perms = set([self.user_perms['repositories_groups'][group_name]])
940 user_perms = set([self.user_perms['repositories_groups'][group_name]])
952 except KeyError:
941 except KeyError:
953 return False
942 return False
954
943
955 if self.required_perms.intersection(user_perms):
944 if self.required_perms.intersection(user_perms):
956 return True
945 return True
957 return False
946 return False
958
947
959
948
960 class HasUserGroupPermissionAllDecorator(PermsDecorator):
949 class HasUserGroupPermissionAllDecorator(PermsDecorator):
961 """
950 """
962 Checks for access permission for all given predicates for specific
951 Checks for access permission for all given predicates for specific
963 user group. All of them have to be meet in order to fulfill the request
952 user group. All of them have to be meet in order to fulfill the request
964 """
953 """
965
954
966 def check_permissions(self):
955 def check_permissions(self):
967 group_name = get_user_group_slug(request)
956 group_name = get_user_group_slug(request)
968 try:
957 try:
969 user_perms = set([self.user_perms['user_groups'][group_name]])
958 user_perms = set([self.user_perms['user_groups'][group_name]])
970 except KeyError:
959 except KeyError:
971 return False
960 return False
972
961
973 if self.required_perms.issubset(user_perms):
962 if self.required_perms.issubset(user_perms):
974 return True
963 return True
975 return False
964 return False
976
965
977
966
978 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
967 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
979 """
968 """
980 Checks for access permission for any of given predicates for specific
969 Checks for access permission for any of given predicates for specific
981 user group. In order to fulfill the request any of predicates must be meet
970 user group. In order to fulfill the request any of predicates must be meet
982 """
971 """
983
972
984 def check_permissions(self):
973 def check_permissions(self):
985 group_name = get_user_group_slug(request)
974 group_name = get_user_group_slug(request)
986 try:
975 try:
987 user_perms = set([self.user_perms['user_groups'][group_name]])
976 user_perms = set([self.user_perms['user_groups'][group_name]])
988 except KeyError:
977 except KeyError:
989 return False
978 return False
990
979
991 if self.required_perms.intersection(user_perms):
980 if self.required_perms.intersection(user_perms):
992 return True
981 return True
993 return False
982 return False
994
983
995
984
996 #==============================================================================
985 #==============================================================================
997 # CHECK FUNCTIONS
986 # CHECK FUNCTIONS
998 #==============================================================================
987 #==============================================================================
999 class PermsFunction(object):
988 class PermsFunction(object):
1000 """Base function for other check functions"""
989 """Base function for other check functions"""
1001
990
1002 def __init__(self, *perms):
991 def __init__(self, *perms):
1003 self.required_perms = set(perms)
992 self.required_perms = set(perms)
1004 self.user_perms = None
993 self.user_perms = None
1005 self.repo_name = None
994 self.repo_name = None
1006 self.group_name = None
995 self.group_name = None
1007
996
1008 def __call__(self, check_location='', user=None):
997 def __call__(self, check_location='', user=None):
1009 if not user:
998 if not user:
1010 #TODO: remove this someday,put as user as attribute here
999 #TODO: remove this someday,put as user as attribute here
1011 user = request.user
1000 user = request.user
1012
1001
1013 # init auth user if not already given
1002 # init auth user if not already given
1014 if not isinstance(user, AuthUser):
1003 if not isinstance(user, AuthUser):
1015 user = AuthUser(user.user_id)
1004 user = AuthUser(user.user_id)
1016
1005
1017 cls_name = self.__class__.__name__
1006 cls_name = self.__class__.__name__
1018 check_scope = {
1007 check_scope = {
1019 'HasPermissionAll': '',
1008 'HasPermissionAll': '',
1020 'HasPermissionAny': '',
1009 'HasPermissionAny': '',
1021 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
1010 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
1022 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
1011 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
1023 'HasRepoGroupPermissionAll': 'group:%s' % self.group_name,
1012 'HasRepoGroupPermissionAll': 'group:%s' % self.group_name,
1024 'HasRepoGroupPermissionAny': 'group:%s' % self.group_name,
1013 'HasRepoGroupPermissionAny': 'group:%s' % self.group_name,
1025 }.get(cls_name, '?')
1014 }.get(cls_name, '?')
1026 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
1015 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
1027 self.required_perms, user, check_scope,
1016 self.required_perms, user, check_scope,
1028 check_location or 'unspecified location')
1017 check_location or 'unspecified location')
1029 if not user:
1018 if not user:
1030 log.debug('Empty request user')
1019 log.debug('Empty request user')
1031 return False
1020 return False
1032 self.user_perms = user.permissions
1021 self.user_perms = user.permissions
1033 if self.check_permissions():
1022 if self.check_permissions():
1034 log.debug('Permission to %s granted for user: %s @ %s'
1023 log.debug('Permission to %s granted for user: %s @ %s'
1035 % (check_scope, user,
1024 % (check_scope, user,
1036 check_location or 'unspecified location'))
1025 check_location or 'unspecified location'))
1037 return True
1026 return True
1038
1027
1039 else:
1028 else:
1040 log.debug('Permission to %s denied for user: %s @ %s'
1029 log.debug('Permission to %s denied for user: %s @ %s'
1041 % (check_scope, user,
1030 % (check_scope, user,
1042 check_location or 'unspecified location'))
1031 check_location or 'unspecified location'))
1043 return False
1032 return False
1044
1033
1045 def check_permissions(self):
1034 def check_permissions(self):
1046 """Dummy function for overriding"""
1035 """Dummy function for overriding"""
1047 raise Exception('You have to write this function in child class')
1036 raise Exception('You have to write this function in child class')
1048
1037
1049
1038
1050 class HasPermissionAll(PermsFunction):
1039 class HasPermissionAll(PermsFunction):
1051 def check_permissions(self):
1040 def check_permissions(self):
1052 if self.required_perms.issubset(self.user_perms.get('global')):
1041 if self.required_perms.issubset(self.user_perms.get('global')):
1053 return True
1042 return True
1054 return False
1043 return False
1055
1044
1056
1045
1057 class HasPermissionAny(PermsFunction):
1046 class HasPermissionAny(PermsFunction):
1058 def check_permissions(self):
1047 def check_permissions(self):
1059 if self.required_perms.intersection(self.user_perms.get('global')):
1048 if self.required_perms.intersection(self.user_perms.get('global')):
1060 return True
1049 return True
1061 return False
1050 return False
1062
1051
1063
1052
1064 class HasRepoPermissionAll(PermsFunction):
1053 class HasRepoPermissionAll(PermsFunction):
1065 def __call__(self, repo_name=None, check_location='', user=None):
1054 def __call__(self, repo_name=None, check_location='', user=None):
1066 self.repo_name = repo_name
1055 self.repo_name = repo_name
1067 return super(HasRepoPermissionAll, self).__call__(check_location, user)
1056 return super(HasRepoPermissionAll, self).__call__(check_location, user)
1068
1057
1069 def check_permissions(self):
1058 def check_permissions(self):
1070 if not self.repo_name:
1059 if not self.repo_name:
1071 self.repo_name = get_repo_slug(request)
1060 self.repo_name = get_repo_slug(request)
1072
1061
1073 try:
1062 try:
1074 self._user_perms = set(
1063 self._user_perms = set(
1075 [self.user_perms['repositories'][self.repo_name]]
1064 [self.user_perms['repositories'][self.repo_name]]
1076 )
1065 )
1077 except KeyError:
1066 except KeyError:
1078 return False
1067 return False
1079 if self.required_perms.issubset(self._user_perms):
1068 if self.required_perms.issubset(self._user_perms):
1080 return True
1069 return True
1081 return False
1070 return False
1082
1071
1083
1072
1084 class HasRepoPermissionAny(PermsFunction):
1073 class HasRepoPermissionAny(PermsFunction):
1085 def __call__(self, repo_name=None, check_location='', user=None):
1074 def __call__(self, repo_name=None, check_location='', user=None):
1086 self.repo_name = repo_name
1075 self.repo_name = repo_name
1087 return super(HasRepoPermissionAny, self).__call__(check_location, user)
1076 return super(HasRepoPermissionAny, self).__call__(check_location, user)
1088
1077
1089 def check_permissions(self):
1078 def check_permissions(self):
1090 if not self.repo_name:
1079 if not self.repo_name:
1091 self.repo_name = get_repo_slug(request)
1080 self.repo_name = get_repo_slug(request)
1092
1081
1093 try:
1082 try:
1094 self._user_perms = set(
1083 self._user_perms = set(
1095 [self.user_perms['repositories'][self.repo_name]]
1084 [self.user_perms['repositories'][self.repo_name]]
1096 )
1085 )
1097 except KeyError:
1086 except KeyError:
1098 return False
1087 return False
1099 if self.required_perms.intersection(self._user_perms):
1088 if self.required_perms.intersection(self._user_perms):
1100 return True
1089 return True
1101 return False
1090 return False
1102
1091
1103
1092
1104 class HasRepoGroupPermissionAny(PermsFunction):
1093 class HasRepoGroupPermissionAny(PermsFunction):
1105 def __call__(self, group_name=None, check_location='', user=None):
1094 def __call__(self, group_name=None, check_location='', user=None):
1106 self.group_name = group_name
1095 self.group_name = group_name
1107 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
1096 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
1108
1097
1109 def check_permissions(self):
1098 def check_permissions(self):
1110 try:
1099 try:
1111 self._user_perms = set(
1100 self._user_perms = set(
1112 [self.user_perms['repositories_groups'][self.group_name]]
1101 [self.user_perms['repositories_groups'][self.group_name]]
1113 )
1102 )
1114 except KeyError:
1103 except KeyError:
1115 return False
1104 return False
1116 if self.required_perms.intersection(self._user_perms):
1105 if self.required_perms.intersection(self._user_perms):
1117 return True
1106 return True
1118 return False
1107 return False
1119
1108
1120
1109
1121 class HasRepoGroupPermissionAll(PermsFunction):
1110 class HasRepoGroupPermissionAll(PermsFunction):
1122 def __call__(self, group_name=None, check_location='', user=None):
1111 def __call__(self, group_name=None, check_location='', user=None):
1123 self.group_name = group_name
1112 self.group_name = group_name
1124 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
1113 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
1125
1114
1126 def check_permissions(self):
1115 def check_permissions(self):
1127 try:
1116 try:
1128 self._user_perms = set(
1117 self._user_perms = set(
1129 [self.user_perms['repositories_groups'][self.group_name]]
1118 [self.user_perms['repositories_groups'][self.group_name]]
1130 )
1119 )
1131 except KeyError:
1120 except KeyError:
1132 return False
1121 return False
1133 if self.required_perms.issubset(self._user_perms):
1122 if self.required_perms.issubset(self._user_perms):
1134 return True
1123 return True
1135 return False
1124 return False
1136
1125
1137
1126
1138 class HasUserGroupPermissionAny(PermsFunction):
1127 class HasUserGroupPermissionAny(PermsFunction):
1139 def __call__(self, user_group_name=None, check_location='', user=None):
1128 def __call__(self, user_group_name=None, check_location='', user=None):
1140 self.user_group_name = user_group_name
1129 self.user_group_name = user_group_name
1141 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
1130 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
1142
1131
1143 def check_permissions(self):
1132 def check_permissions(self):
1144 try:
1133 try:
1145 self._user_perms = set(
1134 self._user_perms = set(
1146 [self.user_perms['user_groups'][self.user_group_name]]
1135 [self.user_perms['user_groups'][self.user_group_name]]
1147 )
1136 )
1148 except KeyError:
1137 except KeyError:
1149 return False
1138 return False
1150 if self.required_perms.intersection(self._user_perms):
1139 if self.required_perms.intersection(self._user_perms):
1151 return True
1140 return True
1152 return False
1141 return False
1153
1142
1154
1143
1155 class HasUserGroupPermissionAll(PermsFunction):
1144 class HasUserGroupPermissionAll(PermsFunction):
1156 def __call__(self, user_group_name=None, check_location='', user=None):
1145 def __call__(self, user_group_name=None, check_location='', user=None):
1157 self.user_group_name = user_group_name
1146 self.user_group_name = user_group_name
1158 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
1147 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
1159
1148
1160 def check_permissions(self):
1149 def check_permissions(self):
1161 try:
1150 try:
1162 self._user_perms = set(
1151 self._user_perms = set(
1163 [self.user_perms['user_groups'][self.user_group_name]]
1152 [self.user_perms['user_groups'][self.user_group_name]]
1164 )
1153 )
1165 except KeyError:
1154 except KeyError:
1166 return False
1155 return False
1167 if self.required_perms.issubset(self._user_perms):
1156 if self.required_perms.issubset(self._user_perms):
1168 return True
1157 return True
1169 return False
1158 return False
1170
1159
1171
1160
1172 #==============================================================================
1161 #==============================================================================
1173 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
1162 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
1174 #==============================================================================
1163 #==============================================================================
1175 class HasPermissionAnyMiddleware(object):
1164 class HasPermissionAnyMiddleware(object):
1176 def __init__(self, *perms):
1165 def __init__(self, *perms):
1177 self.required_perms = set(perms)
1166 self.required_perms = set(perms)
1178
1167
1179 def __call__(self, user, repo_name):
1168 def __call__(self, user, repo_name):
1180 # repo_name MUST be unicode, since we handle keys in permission
1169 # repo_name MUST be unicode, since we handle keys in permission
1181 # dict by unicode
1170 # dict by unicode
1182 repo_name = safe_unicode(repo_name)
1171 repo_name = safe_unicode(repo_name)
1183 usr = AuthUser(user.user_id)
1172 usr = AuthUser(user.user_id)
1184 self.user_perms = set([usr.permissions['repositories'][repo_name]])
1173 self.user_perms = set([usr.permissions['repositories'][repo_name]])
1185 self.username = user.username
1174 self.username = user.username
1186 self.repo_name = repo_name
1175 self.repo_name = repo_name
1187 return self.check_permissions()
1176 return self.check_permissions()
1188
1177
1189 def check_permissions(self):
1178 def check_permissions(self):
1190 log.debug('checking VCS protocol '
1179 log.debug('checking VCS protocol '
1191 'permissions %s for user:%s repository:%s', self.user_perms,
1180 'permissions %s for user:%s repository:%s', self.user_perms,
1192 self.username, self.repo_name)
1181 self.username, self.repo_name)
1193 if self.required_perms.intersection(self.user_perms):
1182 if self.required_perms.intersection(self.user_perms):
1194 log.debug('Permission to repo: %s granted for user: %s @ %s'
1183 log.debug('Permission to repo: %s granted for user: %s @ %s'
1195 % (self.repo_name, self.username, 'PermissionMiddleware'))
1184 % (self.repo_name, self.username, 'PermissionMiddleware'))
1196 return True
1185 return True
1197 log.debug('Permission to repo: %s denied for user: %s @ %s'
1186 log.debug('Permission to repo: %s denied for user: %s @ %s'
1198 % (self.repo_name, self.username, 'PermissionMiddleware'))
1187 % (self.repo_name, self.username, 'PermissionMiddleware'))
1199 return False
1188 return False
1200
1189
1201
1190
1202 #==============================================================================
1191 #==============================================================================
1203 # SPECIAL VERSION TO HANDLE API AUTH
1192 # SPECIAL VERSION TO HANDLE API AUTH
1204 #==============================================================================
1193 #==============================================================================
1205 class _BaseApiPerm(object):
1194 class _BaseApiPerm(object):
1206 def __init__(self, *perms):
1195 def __init__(self, *perms):
1207 self.required_perms = set(perms)
1196 self.required_perms = set(perms)
1208
1197
1209 def __call__(self, check_location=None, user=None, repo_name=None,
1198 def __call__(self, check_location=None, user=None, repo_name=None,
1210 group_name=None):
1199 group_name=None):
1211 cls_name = self.__class__.__name__
1200 cls_name = self.__class__.__name__
1212 check_scope = 'user:%s' % (user)
1201 check_scope = 'user:%s' % (user)
1213 if repo_name:
1202 if repo_name:
1214 check_scope += ', repo:%s' % (repo_name)
1203 check_scope += ', repo:%s' % (repo_name)
1215
1204
1216 if group_name:
1205 if group_name:
1217 check_scope += ', repo group:%s' % (group_name)
1206 check_scope += ', repo group:%s' % (group_name)
1218
1207
1219 log.debug('checking cls:%s %s %s @ %s'
1208 log.debug('checking cls:%s %s %s @ %s'
1220 % (cls_name, self.required_perms, check_scope, check_location))
1209 % (cls_name, self.required_perms, check_scope, check_location))
1221 if not user:
1210 if not user:
1222 log.debug('Empty User passed into arguments')
1211 log.debug('Empty User passed into arguments')
1223 return False
1212 return False
1224
1213
1225 ## process user
1214 ## process user
1226 if not isinstance(user, AuthUser):
1215 if not isinstance(user, AuthUser):
1227 user = AuthUser(user.user_id)
1216 user = AuthUser(user.user_id)
1228 if not check_location:
1217 if not check_location:
1229 check_location = 'unspecified'
1218 check_location = 'unspecified'
1230 if self.check_permissions(user.permissions, repo_name, group_name):
1219 if self.check_permissions(user.permissions, repo_name, group_name):
1231 log.debug('Permission to %s granted for user: %s @ %s'
1220 log.debug('Permission to %s granted for user: %s @ %s'
1232 % (check_scope, user, check_location))
1221 % (check_scope, user, check_location))
1233 return True
1222 return True
1234
1223
1235 else:
1224 else:
1236 log.debug('Permission to %s denied for user: %s @ %s'
1225 log.debug('Permission to %s denied for user: %s @ %s'
1237 % (check_scope, user, check_location))
1226 % (check_scope, user, check_location))
1238 return False
1227 return False
1239
1228
1240 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1229 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1241 """
1230 """
1242 implement in child class should return True if permissions are ok,
1231 implement in child class should return True if permissions are ok,
1243 False otherwise
1232 False otherwise
1244
1233
1245 :param perm_defs: dict with permission definitions
1234 :param perm_defs: dict with permission definitions
1246 :param repo_name: repo name
1235 :param repo_name: repo name
1247 """
1236 """
1248 raise NotImplementedError()
1237 raise NotImplementedError()
1249
1238
1250
1239
1251 class HasPermissionAllApi(_BaseApiPerm):
1240 class HasPermissionAllApi(_BaseApiPerm):
1252 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1241 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1253 if self.required_perms.issubset(perm_defs.get('global')):
1242 if self.required_perms.issubset(perm_defs.get('global')):
1254 return True
1243 return True
1255 return False
1244 return False
1256
1245
1257
1246
1258 class HasPermissionAnyApi(_BaseApiPerm):
1247 class HasPermissionAnyApi(_BaseApiPerm):
1259 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1248 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1260 if self.required_perms.intersection(perm_defs.get('global')):
1249 if self.required_perms.intersection(perm_defs.get('global')):
1261 return True
1250 return True
1262 return False
1251 return False
1263
1252
1264
1253
1265 class HasRepoPermissionAllApi(_BaseApiPerm):
1254 class HasRepoPermissionAllApi(_BaseApiPerm):
1266 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1255 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1267 try:
1256 try:
1268 _user_perms = set([perm_defs['repositories'][repo_name]])
1257 _user_perms = set([perm_defs['repositories'][repo_name]])
1269 except KeyError:
1258 except KeyError:
1270 log.warning(traceback.format_exc())
1259 log.warning(traceback.format_exc())
1271 return False
1260 return False
1272 if self.required_perms.issubset(_user_perms):
1261 if self.required_perms.issubset(_user_perms):
1273 return True
1262 return True
1274 return False
1263 return False
1275
1264
1276
1265
1277 class HasRepoPermissionAnyApi(_BaseApiPerm):
1266 class HasRepoPermissionAnyApi(_BaseApiPerm):
1278 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1267 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1279 try:
1268 try:
1280 _user_perms = set([perm_defs['repositories'][repo_name]])
1269 _user_perms = set([perm_defs['repositories'][repo_name]])
1281 except KeyError:
1270 except KeyError:
1282 log.warning(traceback.format_exc())
1271 log.warning(traceback.format_exc())
1283 return False
1272 return False
1284 if self.required_perms.intersection(_user_perms):
1273 if self.required_perms.intersection(_user_perms):
1285 return True
1274 return True
1286 return False
1275 return False
1287
1276
1288
1277
1289 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
1278 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
1290 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1279 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1291 try:
1280 try:
1292 _user_perms = set([perm_defs['repositories_groups'][group_name]])
1281 _user_perms = set([perm_defs['repositories_groups'][group_name]])
1293 except KeyError:
1282 except KeyError:
1294 log.warning(traceback.format_exc())
1283 log.warning(traceback.format_exc())
1295 return False
1284 return False
1296 if self.required_perms.intersection(_user_perms):
1285 if self.required_perms.intersection(_user_perms):
1297 return True
1286 return True
1298 return False
1287 return False
1299
1288
1300 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
1289 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
1301 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1290 def check_permissions(self, perm_defs, repo_name=None, group_name=None):
1302 try:
1291 try:
1303 _user_perms = set([perm_defs['repositories_groups'][group_name]])
1292 _user_perms = set([perm_defs['repositories_groups'][group_name]])
1304 except KeyError:
1293 except KeyError:
1305 log.warning(traceback.format_exc())
1294 log.warning(traceback.format_exc())
1306 return False
1295 return False
1307 if self.required_perms.issubset(_user_perms):
1296 if self.required_perms.issubset(_user_perms):
1308 return True
1297 return True
1309 return False
1298 return False
1310
1299
1311 def check_ip_access(source_ip, allowed_ips=None):
1300 def check_ip_access(source_ip, allowed_ips=None):
1312 """
1301 """
1313 Checks if source_ip is a subnet of any of allowed_ips.
1302 Checks if source_ip is a subnet of any of allowed_ips.
1314
1303
1315 :param source_ip:
1304 :param source_ip:
1316 :param allowed_ips: list of allowed ips together with mask
1305 :param allowed_ips: list of allowed ips together with mask
1317 """
1306 """
1318 from kallithea.lib import ipaddr
1307 from kallithea.lib import ipaddr
1319 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1308 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1320 if isinstance(allowed_ips, (tuple, list, set)):
1309 if isinstance(allowed_ips, (tuple, list, set)):
1321 for ip in allowed_ips:
1310 for ip in allowed_ips:
1322 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1311 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1323 log.debug('IP %s is network %s' %
1312 log.debug('IP %s is network %s' %
1324 (ipaddr.IPAddress(source_ip), ipaddr.IPNetwork(ip)))
1313 (ipaddr.IPAddress(source_ip), ipaddr.IPNetwork(ip)))
1325 return True
1314 return True
1326 return False
1315 return False
General Comments 0
You need to be logged in to leave comments. Login now