##// END OF EJS Templates
Full rewrite of auth module, new functions/decorators. FIxed auth user
marcink -
r316:d6e28177 default
parent child Browse files
Show More
@@ -2,7 +2,7 b''
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
@@ -24,15 +24,17 b' Created on April 4, 2010'
24 """
24 """
25
25
26 from functools import wraps
26 from functools import wraps
27 from pylons import session, url, app_globals as g
27 from pylons import session, url, request
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons_app.model import meta
29 from pylons_app.model import meta
30 from pylons_app.model.db import User, Repo2Perm
30 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
31 from pylons_app.lib.utils import get_repo_slug
31 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 import crypt
34 import crypt
34 import logging
35 import logging
35 log = logging.getLogger(__name__)
36 from pylons import config
37 log = logging.getLogger(__name__)
36
38
37 def get_crypt_password(password):
39 def get_crypt_password(password):
38 """
40 """
@@ -64,16 +66,12 b' class AuthUser(object):'
64 """
66 """
65 A simple object that handles a mercurial username for authentication
67 A simple object that handles a mercurial username for authentication
66 """
68 """
67 username = 'None'
68 user_id = None
69 is_authenticated = False
70 is_admin = False
71 permissions = set()
72 group = set()
73
74 def __init__(self):
69 def __init__(self):
75 pass
70 self.username = 'None'
76
71 self.user_id = None
72 self.is_authenticated = False
73 self.is_admin = False
74 self.permissions = {}
77
75
78
76
79 def set_available_permissions(config):
77 def set_available_permissions(config):
@@ -84,107 +82,304 b' def set_available_permissions(config):'
84 ie. to decorate new views with the newly created permission
82 ie. to decorate new views with the newly created permission
85 @param config:
83 @param config:
86 """
84 """
87 from pylons_app.model.meta import Session
85 log.info('getting information about all available permissions')
88 from pylons_app.model.db import Permission
86 sa = meta.Session
89 logging.info('getting information about all available permissions')
90 sa = Session()
91 all_perms = sa.query(Permission).all()
87 all_perms = sa.query(Permission).all()
92 config['pylons.app_globals'].available_permissions = [x.permission_name for x in all_perms]
88 config['available_permissions'] = [x.permission_name for x in all_perms]
89
90 def set_base_path(config):
91 config['base_path'] = config['pylons.app_globals'].base_path
92
93 def fill_perms(user):
94 sa = meta.Session
95 user.permissions['repositories'] = {}
96
97 #first fetch default permissions
98 default_perms = sa.query(Repo2Perm, Repository, Permission)\
99 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
100 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
101 .filter(Repo2Perm.user_id == sa.query(User).filter(User.username ==
102 'default').one().user_id).all()
93
103
104 if user.is_admin:
105 user.permissions['global'] = set(['hg.admin'])
106 #admin have all rights full
107 for perm in default_perms:
108 p = 'repository.admin'
109 user.permissions['repositories'][perm.Repo2Perm.repository] = p
110
111 else:
112 user.permissions['global'] = set()
113 for perm in default_perms:
114 if perm.Repository.private:
115 #disable defaults for private repos,
116 p = 'repository.none'
117 elif perm.Repository.user_id == user.user_id:
118 #set admin if owner
119 p = 'repository.admin'
120 else:
121 p = perm.Permission.permission_name
122
123 user.permissions['repositories'][perm.Repo2Perm.repository] = p
124
125
126 user_perms = sa.query(Repo2Perm, Permission, Repository)\
127 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
128 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
129 .filter(Repo2Perm.user_id == user.user_id).all()
130 #overwrite userpermissions with defaults
131 for perm in user_perms:
132 #set write if owner
133 if perm.Repository.user_id == user.user_id:
134 p = 'repository.write'
135 else:
136 p = perm.Permission.permission_name
137 user.permissions['repositories'][perm.Repo2Perm.repository] = p
138 return user
139
94 def get_user(session):
140 def get_user(session):
95 """
141 """
96 Gets user from session, and wraps permissions into user
142 Gets user from session, and wraps permissions into user
97 @param session:
143 @param session:
98 """
144 """
99 user = session.get('hg_app_user', AuthUser())
145 user = session.get('hg_app_user', AuthUser())
146
100 if user.is_authenticated:
147 if user.is_authenticated:
101 sa = meta.Session
148 user = fill_perms(user)
102 user.permissions = sa.query(Repo2Perm)\
149
103 .filter(Repo2Perm.user_id == user.user_id).all()
150 session['hg_app_user'] = user
104
151 session.save()
105 return user
152 return user
106
153
107 #===============================================================================
154 #===============================================================================
108 # DECORATORS
155 # CHECK DECORATORS
109 #===============================================================================
156 #===============================================================================
110 class LoginRequired(object):
157 class LoginRequired(object):
111 """
158 """
112 Must be logged in to execute this function else redirect to login page
159 Must be logged in to execute this function else redirect to login page
113 """
160 """
114 def __init__(self):
161
115 pass
116
117 def __call__(self, func):
162 def __call__(self, func):
118
119 @wraps(func)
163 @wraps(func)
120 def _wrapper(*fargs, **fkwargs):
164 def _wrapper(*fargs, **fkwargs):
121 user = session.get('hg_app_user', AuthUser())
165 user = session.get('hg_app_user', AuthUser())
122 log.info('Checking login required for user:%s', user.username)
166 log.debug('Checking login required for user:%s', user.username)
123 if user.is_authenticated:
167 if user.is_authenticated:
124 log.info('user %s is authenticated', user.username)
168 log.debug('user %s is authenticated', user.username)
125 func(*fargs)
169 func(*fargs)
126 else:
170 else:
127 logging.info('user %s not authenticated', user.username)
171 log.warn('user %s not authenticated', user.username)
128 logging.info('redirecting to login page')
172 log.debug('redirecting to login page')
129 return redirect(url('login_home'))
173 return redirect(url('login_home'))
130
174
131 return _wrapper
175 return _wrapper
132
176
133 class PermsDecorator(object):
177 class PermsDecorator(object):
178 """
179 Base class for decorators
180 """
134
181
135 def __init__(self, *perms):
182 def __init__(self, *required_perms):
136 available_perms = g.available_permissions
183 available_perms = config['available_permissions']
137 for perm in perms:
184 for perm in required_perms:
138 if perm not in available_perms:
185 if perm not in available_perms:
139 raise Exception("'%s' permission in not defined" % perm)
186 raise Exception("'%s' permission is not defined" % perm)
140 self.required_perms = set(perms)
187 self.required_perms = set(required_perms)
141 self.user_perms = set([])#propagate this list from somewhere.
188 self.user_perms = None
142
189
143 def __call__(self, func):
190 def __call__(self, func):
144 @wraps(func)
191 @wraps(func)
145 def _wrapper(*args, **kwargs):
192 def _wrapper(*fargs, **fkwargs):
146 logging.info('checking %s permissions %s for %s',
193 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
147 self.__class__.__name__[-3:], self.required_perms, func.__name__)
194 log.debug('checking %s permissions %s for %s',
195 self.__class__.__name__, self.required_perms, func.__name__)
148
196
149 if self.check_permissions():
197 if self.check_permissions():
150 logging.info('Permission granted for %s', func.__name__)
198 log.debug('Permission granted for %s', func.__name__)
151 return func(*args, **kwargs)
199 return func(*fargs)
152
200
153 else:
201 else:
154 logging.warning('Permission denied for %s', func.__name__)
202 log.warning('Permission denied for %s', func.__name__)
155 #redirect with forbidden ret code
203 #redirect with forbidden ret code
156 return redirect(url('access_denied'), 403)
204 return abort(403)
157 return _wrapper
205 return _wrapper
158
206
159
207
160 def check_permissions(self):
208 def check_permissions(self):
161 """
209 """
162 Dummy function for overiding
210 Dummy function for overriding
163 """
211 """
164 raise Exception('You have to write this function in child class')
212 raise Exception('You have to write this function in child class')
165
213
166 class CheckPermissionAll(PermsDecorator):
214 class HasPermissionAllDecorator(PermsDecorator):
167 """
215 """
168 Checks for access permission for all given predicates. All of them have to
216 Checks for access permission for all given predicates. All of them have to
169 be meet in order to fulfill the request
217 be meet in order to fulfill the request
170 """
218 """
171
219
172 def check_permissions(self):
220 def check_permissions(self):
173 if self.required_perms.issubset(self.user_perms):
221 if self.required_perms.issubset(self.user_perms['global']):
174 return True
222 return True
175 return False
223 return False
176
224
177
225
178 class CheckPermissionAny(PermsDecorator):
226 class HasPermissionAnyDecorator(PermsDecorator):
179 """
227 """
180 Checks for access permission for any of given predicates. In order to
228 Checks for access permission for any of given predicates. In order to
181 fulfill the request any of predicates must be meet
229 fulfill the request any of predicates must be meet
182 """
230 """
183
231
184 def check_permissions(self):
232 def check_permissions(self):
233 if self.required_perms.intersection(self.user_perms['global']):
234 return True
235 return False
236
237 class HasRepoPermissionAllDecorator(PermsDecorator):
238 """
239 Checks for access permission for all given predicates for specific
240 repository. All of them have to be meet in order to fulfill the request
241 """
242
243 def check_permissions(self):
244 repo_name = get_repo_slug(request)
245 user_perms = set([self.user_perms['repositories'][repo_name]])
246 if self.required_perms.issubset(user_perms):
247 return True
248 return False
249
250
251 class HasRepoPermissionAnyDecorator(PermsDecorator):
252 """
253 Checks for access permission for any of given predicates for specific
254 repository. In order to fulfill the request any of predicates must be meet
255 """
256
257 def check_permissions(self):
258 repo_name = get_repo_slug(request)
259
260 user_perms = set([self.user_perms['repositories'][repo_name]])
261 if self.required_perms.intersection(user_perms):
262 return True
263 return False
264 #===============================================================================
265 # CHECK FUNCTIONS
266 #===============================================================================
267
268 class PermsFunction(object):
269 """
270 Base function for other check functions
271 """
272
273 def __init__(self, *perms):
274 available_perms = config['available_permissions']
275
276 for perm in perms:
277 if perm not in available_perms:
278 raise Exception("'%s' permission in not defined" % perm)
279 self.required_perms = set(perms)
280 self.user_perms = None
281 self.granted_for = ''
282 self.repo_name = None
283
284 def __call__(self, check_Location=''):
285 user = session['hg_app_user']
286 self.user_perms = user.permissions
287 self.granted_for = user.username
288 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
289
290 if self.check_permissions():
291 log.debug('Permission granted for %s @%s', self.granted_for,
292 check_Location)
293 return True
294
295 else:
296 log.warning('Permission denied for %s @%s', self.granted_for,
297 check_Location)
298 return False
299
300 def check_permissions(self):
301 """
302 Dummy function for overriding
303 """
304 raise Exception('You have to write this function in child class')
305
306 class HasPermissionAll(PermsFunction):
307 def check_permissions(self):
308 if self.required_perms.issubset(self.user_perms['global']):
309 return True
310 return False
311
312 class HasPermissionAny(PermsFunction):
313 def check_permissions(self):
314 if self.required_perms.intersection(self.user_perms['global']):
315 return True
316 return False
317
318 class HasRepoPermissionAll(PermsFunction):
319
320 def __call__(self, repo_name=None, check_Location=''):
321 self.repo_name = repo_name
322 return super(HasRepoPermissionAll, self).__call__(check_Location)
323
324 def check_permissions(self):
325 if not self.repo_name:
326 self.repo_name = get_repo_slug(request)
327
328 self.user_perms = set([self.user_perms['repositories']\
329 .get(self.repo_name)])
330 self.granted_for = self.repo_name
331 if self.required_perms.issubset(self.user_perms):
332 return True
333 return False
334
335 class HasRepoPermissionAny(PermsFunction):
336
337
338 def __call__(self, repo_name=None, check_Location=''):
339 self.repo_name = repo_name
340 return super(HasRepoPermissionAny, self).__call__(check_Location)
341
342 def check_permissions(self):
343 if not self.repo_name:
344 self.repo_name = get_repo_slug(request)
345
346 self.user_perms = set([self.user_perms['repositories']\
347 .get(self.repo_name)])
348 self.granted_for = self.repo_name
185 if self.required_perms.intersection(self.user_perms):
349 if self.required_perms.intersection(self.user_perms):
186 return True
350 return True
187 return False
351 return False
188
352
353 #===============================================================================
354 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
355 #===============================================================================
189
356
190
357 class HasPermissionAnyMiddleware(object):
358 def __init__(self, *perms):
359 self.required_perms = set(perms)
360
361 def __call__(self, user, repo_name):
362 usr = AuthUser()
363 usr.user_id = user.user_id
364 usr.username = user.username
365 usr.is_admin = user.admin
366
367 try:
368 self.user_perms = set([fill_perms(usr)\
369 .permissions['repositories'][repo_name]])
370 except:
371 self.user_perms = set()
372 self.granted_for = ''
373 self.username = user.username
374 self.repo_name = repo_name
375 return self.check_permissions()
376
377 def check_permissions(self):
378 log.debug('checking mercurial protocol '
379 'permissions for user:%s repository:%s',
380 self.username, self.repo_name)
381 if self.required_perms.intersection(self.user_perms):
382 log.debug('permission granted')
383 return True
384 log.debug('permission denied')
385 return False
General Comments 0
You need to be logged in to leave comments. Login now