comparison pylons_app/lib/auth.py @ 377:bd8b25ad058d

Fixed decorators bug when using them with keyworded arguments,new implementation takes new approach that is more flexible
author Marcin Kuzminski <marcin@python-works.com>
date Sat, 31 Jul 2010 18:47:43 +0200
parents 5cd6616b8673
children ca54622e39a1
comparison
equal deleted inserted replaced
376:7fbf81447c6c 377:bd8b25ad058d
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # encoding: utf-8 2 # encoding: utf-8
3 # authentication and permission libraries 3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 # This program is free software; you can redistribute it and/or 6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License 7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2 8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license. 9 # of the License or (at your opinion) any later version of the license.
10 # 10 #
15 # 15 #
16 # You should have received a copy of the GNU General Public License 16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software 17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA. 19 # MA 02110-1301, USA.
20 """
21 Created on April 4, 2010
22
23 @author: marcink
24 """
25 from beaker.cache import cache_region 20 from beaker.cache import cache_region
26 from functools import wraps
27 from pylons import config, session, url, request 21 from pylons import config, session, url, request
28 from pylons.controllers.util import abort, redirect 22 from pylons.controllers.util import abort, redirect
29 from pylons_app.lib.utils import get_repo_slug 23 from pylons_app.lib.utils import get_repo_slug
30 from pylons_app.model import meta 24 from pylons_app.model import meta
31 from pylons_app.model.db import User, Repo2Perm, Repository, Permission 25 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
32 from sqlalchemy.exc import OperationalError 26 from sqlalchemy.exc import OperationalError
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound 27 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 import crypt 28 import crypt
29 from decorator import decorator
35 import logging 30 import logging
31 """
32 Created on April 4, 2010
33
34 @author: marcink
35 """
36 36
37 log = logging.getLogger(__name__) 37 log = logging.getLogger(__name__)
38 38
39 def get_crypt_password(password): 39 def get_crypt_password(password):
40 """ 40 """
144 for perm in default_perms: 144 for perm in default_perms:
145 p = 'repository.admin' 145 p = 'repository.admin'
146 user.permissions['repositories'][perm.Repo2Perm.repository.repo_name] = p 146 user.permissions['repositories'][perm.Repo2Perm.repository.repo_name] = p
147 147
148 else: 148 else:
149 user.permissions['global'].add('') 149 user.permissions['global'].add('repository.create')
150 for perm in default_perms: 150 for perm in default_perms:
151 if perm.Repository.private: 151 if perm.Repository.private:
152 #disable defaults for private repos, 152 #disable defaults for private repos,
153 p = 'repository.none' 153 p = 'repository.none'
154 elif perm.Repository.user_id == user.user_id: 154 elif perm.Repository.user_id == user.user_id:
190 190
191 #=============================================================================== 191 #===============================================================================
192 # CHECK DECORATORS 192 # CHECK DECORATORS
193 #=============================================================================== 193 #===============================================================================
194 class LoginRequired(object): 194 class LoginRequired(object):
195 """ 195 """Must be logged in to execute this function else redirect to login page"""
196 Must be logged in to execute this function else redirect to login page
197 """
198 196
199 def __call__(self, func): 197 def __call__(self, func):
200 @wraps(func) 198 return decorator(self.__wrapper, func)
201 def _wrapper(*fargs, **fkwargs): 199
202 user = session.get('hg_app_user', AuthUser()) 200 def __wrapper(self, func, *fargs, **fkwargs):
203 log.debug('Checking login required for user:%s', user.username) 201 user = session.get('hg_app_user', AuthUser())
204 if user.is_authenticated: 202 log.debug('Checking login required for user:%s', user.username)
205 log.debug('user %s is authenticated', user.username) 203 if user.is_authenticated:
206 func(*fargs) 204 log.debug('user %s is authenticated', user.username)
207 else: 205 return func(*fargs, **fkwargs)
208 log.warn('user %s not authenticated', user.username) 206 else:
209 log.debug('redirecting to login page') 207 log.warn('user %s not authenticated', user.username)
210 return redirect(url('login_home')) 208 log.debug('redirecting to login page')
211 209 return redirect(url('login_home'))
212 return _wrapper
213 210
214 class PermsDecorator(object): 211 class PermsDecorator(object):
215 """ 212 """Base class for decorators"""
216 Base class for decorators
217 """
218 213
219 def __init__(self, *required_perms): 214 def __init__(self, *required_perms):
220 available_perms = config['available_permissions'] 215 available_perms = config['available_permissions']
221 for perm in required_perms: 216 for perm in required_perms:
222 if perm not in available_perms: 217 if perm not in available_perms:
223 raise Exception("'%s' permission is not defined" % perm) 218 raise Exception("'%s' permission is not defined" % perm)
224 self.required_perms = set(required_perms) 219 self.required_perms = set(required_perms)
225 self.user_perms = None 220 self.user_perms = None
226 221
227 def __call__(self, func): 222 def __call__(self, func):
228 @wraps(func) 223 return decorator(self.__wrapper, func)
229 def _wrapper(*fargs, **fkwargs): 224
230 self.user_perms = session.get('hg_app_user', AuthUser()).permissions 225
231 log.debug('checking %s permissions %s for %s', 226 def __wrapper(self, func, *fargs, **fkwargs):
232 self.__class__.__name__, self.required_perms, func.__name__) 227 # _wrapper.__name__ = func.__name__
233 228 # _wrapper.__dict__.update(func.__dict__)
234 if self.check_permissions(): 229 # _wrapper.__doc__ = func.__doc__
235 log.debug('Permission granted for %s', func.__name__) 230
236 return func(*fargs) 231 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
237 232 log.debug('checking %s permissions %s for %s',
238 else: 233 self.__class__.__name__, self.required_perms, func.__name__)
239 log.warning('Permission denied for %s', func.__name__) 234
240 #redirect with forbidden ret code 235 if self.check_permissions():
241 return abort(403) 236 log.debug('Permission granted for %s', func.__name__)
242 return _wrapper 237
243 238 return func(*fargs, **fkwargs)
244 239
245 def check_permissions(self): 240 else:
246 """ 241 log.warning('Permission denied for %s', func.__name__)
247 Dummy function for overriding 242 #redirect with forbidden ret code
248 """ 243 return abort(403)
244
245
246
247 def check_permissions(self):
248 """Dummy function for overriding"""
249 raise Exception('You have to write this function in child class') 249 raise Exception('You have to write this function in child class')
250 250
251 class HasPermissionAllDecorator(PermsDecorator): 251 class HasPermissionAllDecorator(PermsDecorator):
252 """ 252 """Checks for access permission for all given predicates. All of them
253 Checks for access permission for all given predicates. All of them have to 253 have to be meet in order to fulfill the request
254 be meet in order to fulfill the request
255 """ 254 """
256 255
257 def check_permissions(self): 256 def check_permissions(self):
258 if self.required_perms.issubset(self.user_perms.get('global')): 257 if self.required_perms.issubset(self.user_perms.get('global')):
259 return True 258 return True
260 return False 259 return False
261 260
262 261
263 class HasPermissionAnyDecorator(PermsDecorator): 262 class HasPermissionAnyDecorator(PermsDecorator):
264 """ 263 """Checks for access permission for any of given predicates. In order to
265 Checks for access permission for any of given predicates. In order to
266 fulfill the request any of predicates must be meet 264 fulfill the request any of predicates must be meet
267 """ 265 """
268 266
269 def check_permissions(self): 267 def check_permissions(self):
270 if self.required_perms.intersection(self.user_perms.get('global')): 268 if self.required_perms.intersection(self.user_perms.get('global')):
271 return True 269 return True
272 return False 270 return False
273 271
274 class HasRepoPermissionAllDecorator(PermsDecorator): 272 class HasRepoPermissionAllDecorator(PermsDecorator):
275 """ 273 """Checks for access permission for all given predicates for specific
276 Checks for access permission for all given predicates for specific
277 repository. All of them have to be meet in order to fulfill the request 274 repository. All of them have to be meet in order to fulfill the request
278 """ 275 """
279 276
280 def check_permissions(self): 277 def check_permissions(self):
281 repo_name = get_repo_slug(request) 278 repo_name = get_repo_slug(request)
287 return True 284 return True
288 return False 285 return False
289 286
290 287
291 class HasRepoPermissionAnyDecorator(PermsDecorator): 288 class HasRepoPermissionAnyDecorator(PermsDecorator):
292 """ 289 """Checks for access permission for any of given predicates for specific
293 Checks for access permission for any of given predicates for specific
294 repository. In order to fulfill the request any of predicates must be meet 290 repository. In order to fulfill the request any of predicates must be meet
295 """ 291 """
296 292
297 def check_permissions(self): 293 def check_permissions(self):
298 repo_name = get_repo_slug(request) 294 repo_name = get_repo_slug(request)
307 #=============================================================================== 303 #===============================================================================
308 # CHECK FUNCTIONS 304 # CHECK FUNCTIONS
309 #=============================================================================== 305 #===============================================================================
310 306
311 class PermsFunction(object): 307 class PermsFunction(object):
312 """ 308 """Base function for other check functions"""
313 Base function for other check functions
314 """
315 309
316 def __init__(self, *perms): 310 def __init__(self, *perms):
317 available_perms = config['available_permissions'] 311 available_perms = config['available_permissions']
318 312
319 for perm in perms: 313 for perm in perms:
341 log.warning('Permission denied for %s @%s', self.granted_for, 335 log.warning('Permission denied for %s @%s', self.granted_for,
342 check_Location) 336 check_Location)
343 return False 337 return False
344 338
345 def check_permissions(self): 339 def check_permissions(self):
346 """ 340 """Dummy function for overriding"""
347 Dummy function for overriding
348 """
349 raise Exception('You have to write this function in child class') 341 raise Exception('You have to write this function in child class')
350 342
351 class HasPermissionAll(PermsFunction): 343 class HasPermissionAll(PermsFunction):
352 def check_permissions(self): 344 def check_permissions(self):
353 if self.required_perms.issubset(self.user_perms.get('global')): 345 if self.required_perms.issubset(self.user_perms.get('global')):
379 if self.required_perms.issubset(self.user_perms): 371 if self.required_perms.issubset(self.user_perms):
380 return True 372 return True
381 return False 373 return False
382 374
383 class HasRepoPermissionAny(PermsFunction): 375 class HasRepoPermissionAny(PermsFunction):
384
385 376
386 def __call__(self, repo_name=None, check_Location=''): 377 def __call__(self, repo_name=None, check_Location=''):
387 self.repo_name = repo_name 378 self.repo_name = repo_name
388 return super(HasRepoPermissionAny, self).__call__(check_Location) 379 return super(HasRepoPermissionAny, self).__call__(check_Location)
389 380