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