##// END OF EJS Templates
added session remove in forms, and added name and lastname to auth user
marcink -
r355:5bbcc0ca default
parent child Browse files
Show More
@@ -1,413 +1,415 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 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from functools import wraps
26 from functools import wraps
27 from pylons import config, session, url, request
27 from pylons import config, session, url, request
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons_app.lib.utils import get_repo_slug
29 from pylons_app.lib.utils import get_repo_slug
30 from pylons_app.model import meta
30 from pylons_app.model import meta
31 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
31 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
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
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 """
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
46
47 @cache_region('super_short_term', 'cached_user')
47 @cache_region('super_short_term', 'cached_user')
48 def get_user_cached(username):
48 def get_user_cached(username):
49 sa = meta.Session
49 sa = meta.Session
50 try:
50 try:
51 user = sa.query(User).filter(User.username == username).one()
51 user = sa.query(User).filter(User.username == username).one()
52 finally:
52 finally:
53 meta.Session.remove()
53 meta.Session.remove()
54 return user
54 return user
55
55
56 def authfunc(environ, username, password):
56 def authfunc(environ, username, password):
57 password_crypt = get_crypt_password(password)
57 password_crypt = get_crypt_password(password)
58 try:
58 try:
59 user = get_user_cached(username)
59 user = get_user_cached(username)
60 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
60 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
61 log.error(e)
61 log.error(e)
62 user = None
62 user = None
63
63
64 if user:
64 if user:
65 if user.active:
65 if user.active:
66 if user.username == username and user.password == password_crypt:
66 if user.username == username and user.password == password_crypt:
67 log.info('user %s authenticated correctly', username)
67 log.info('user %s authenticated correctly', username)
68 return True
68 return True
69 else:
69 else:
70 log.error('user %s is disabled', username)
70 log.error('user %s is disabled', username)
71
71
72 return False
72 return False
73
73
74 class AuthUser(object):
74 class AuthUser(object):
75 """
75 """
76 A simple object that handles a mercurial username for authentication
76 A simple object that handles a mercurial username for authentication
77 """
77 """
78 def __init__(self):
78 def __init__(self):
79 self.username = 'None'
79 self.username = 'None'
80 self.name = ''
81 self.lastname = ''
80 self.user_id = None
82 self.user_id = None
81 self.is_authenticated = False
83 self.is_authenticated = False
82 self.is_admin = False
84 self.is_admin = False
83 self.permissions = {}
85 self.permissions = {}
84
86
85
87
86 def set_available_permissions(config):
88 def set_available_permissions(config):
87 """
89 """
88 This function will propagate pylons globals with all available defined
90 This function will propagate pylons globals with all available defined
89 permission given in db. We don't wannt to check each time from db for new
91 permission given in db. We don't wannt to check each time from db for new
90 permissions since adding a new permission also requires application restart
92 permissions since adding a new permission also requires application restart
91 ie. to decorate new views with the newly created permission
93 ie. to decorate new views with the newly created permission
92 @param config:
94 @param config:
93 """
95 """
94 log.info('getting information about all available permissions')
96 log.info('getting information about all available permissions')
95 try:
97 try:
96 sa = meta.Session
98 sa = meta.Session
97 all_perms = sa.query(Permission).all()
99 all_perms = sa.query(Permission).all()
98 finally:
100 finally:
99 meta.Session.remove()
101 meta.Session.remove()
100
102
101 config['available_permissions'] = [x.permission_name for x in all_perms]
103 config['available_permissions'] = [x.permission_name for x in all_perms]
102
104
103 def set_base_path(config):
105 def set_base_path(config):
104 config['base_path'] = config['pylons.app_globals'].base_path
106 config['base_path'] = config['pylons.app_globals'].base_path
105
107
106 def fill_perms(user):
108 def fill_perms(user):
107 sa = meta.Session
109 sa = meta.Session
108 user.permissions['repositories'] = {}
110 user.permissions['repositories'] = {}
109
111
110 #first fetch default permissions
112 #first fetch default permissions
111 default_perms = sa.query(Repo2Perm, Repository, Permission)\
113 default_perms = sa.query(Repo2Perm, Repository, Permission)\
112 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
114 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
113 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
115 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
114 .filter(Repo2Perm.user_id == sa.query(User).filter(User.username ==
116 .filter(Repo2Perm.user_id == sa.query(User).filter(User.username ==
115 'default').one().user_id).all()
117 'default').one().user_id).all()
116
118
117 if user.is_admin:
119 if user.is_admin:
118 user.permissions['global'] = set(['hg.admin'])
120 user.permissions['global'] = set(['hg.admin'])
119 #admin have all rights full
121 #admin have all rights full
120 for perm in default_perms:
122 for perm in default_perms:
121 p = 'repository.admin'
123 p = 'repository.admin'
122 user.permissions['repositories'][perm.Repo2Perm.repository] = p
124 user.permissions['repositories'][perm.Repo2Perm.repository] = p
123
125
124 else:
126 else:
125 user.permissions['global'] = set()
127 user.permissions['global'] = set()
126 for perm in default_perms:
128 for perm in default_perms:
127 if perm.Repository.private:
129 if perm.Repository.private:
128 #disable defaults for private repos,
130 #disable defaults for private repos,
129 p = 'repository.none'
131 p = 'repository.none'
130 elif perm.Repository.user_id == user.user_id:
132 elif perm.Repository.user_id == user.user_id:
131 #set admin if owner
133 #set admin if owner
132 p = 'repository.admin'
134 p = 'repository.admin'
133 else:
135 else:
134 p = perm.Permission.permission_name
136 p = perm.Permission.permission_name
135
137
136 user.permissions['repositories'][perm.Repo2Perm.repository] = p
138 user.permissions['repositories'][perm.Repo2Perm.repository] = p
137
139
138
140
139 user_perms = sa.query(Repo2Perm, Permission, Repository)\
141 user_perms = sa.query(Repo2Perm, Permission, Repository)\
140 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
142 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
141 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
143 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
142 .filter(Repo2Perm.user_id == user.user_id).all()
144 .filter(Repo2Perm.user_id == user.user_id).all()
143 #overwrite userpermissions with defaults
145 #overwrite userpermissions with defaults
144 for perm in user_perms:
146 for perm in user_perms:
145 #set write if owner
147 #set write if owner
146 if perm.Repository.user_id == user.user_id:
148 if perm.Repository.user_id == user.user_id:
147 p = 'repository.write'
149 p = 'repository.write'
148 else:
150 else:
149 p = perm.Permission.permission_name
151 p = perm.Permission.permission_name
150 user.permissions['repositories'][perm.Repo2Perm.repository] = p
152 user.permissions['repositories'][perm.Repo2Perm.repository] = p
151 meta.Session.remove()
153 meta.Session.remove()
152 return user
154 return user
153
155
154 def get_user(session):
156 def get_user(session):
155 """
157 """
156 Gets user from session, and wraps permissions into user
158 Gets user from session, and wraps permissions into user
157 @param session:
159 @param session:
158 """
160 """
159 user = session.get('hg_app_user', AuthUser())
161 user = session.get('hg_app_user', AuthUser())
160
162
161 if user.is_authenticated:
163 if user.is_authenticated:
162 user = fill_perms(user)
164 user = fill_perms(user)
163
165
164 session['hg_app_user'] = user
166 session['hg_app_user'] = user
165 session.save()
167 session.save()
166 return user
168 return user
167
169
168 #===============================================================================
170 #===============================================================================
169 # CHECK DECORATORS
171 # CHECK DECORATORS
170 #===============================================================================
172 #===============================================================================
171 class LoginRequired(object):
173 class LoginRequired(object):
172 """
174 """
173 Must be logged in to execute this function else redirect to login page
175 Must be logged in to execute this function else redirect to login page
174 """
176 """
175
177
176 def __call__(self, func):
178 def __call__(self, func):
177 @wraps(func)
179 @wraps(func)
178 def _wrapper(*fargs, **fkwargs):
180 def _wrapper(*fargs, **fkwargs):
179 user = session.get('hg_app_user', AuthUser())
181 user = session.get('hg_app_user', AuthUser())
180 log.debug('Checking login required for user:%s', user.username)
182 log.debug('Checking login required for user:%s', user.username)
181 if user.is_authenticated:
183 if user.is_authenticated:
182 log.debug('user %s is authenticated', user.username)
184 log.debug('user %s is authenticated', user.username)
183 func(*fargs)
185 func(*fargs)
184 else:
186 else:
185 log.warn('user %s not authenticated', user.username)
187 log.warn('user %s not authenticated', user.username)
186 log.debug('redirecting to login page')
188 log.debug('redirecting to login page')
187 return redirect(url('login_home'))
189 return redirect(url('login_home'))
188
190
189 return _wrapper
191 return _wrapper
190
192
191 class PermsDecorator(object):
193 class PermsDecorator(object):
192 """
194 """
193 Base class for decorators
195 Base class for decorators
194 """
196 """
195
197
196 def __init__(self, *required_perms):
198 def __init__(self, *required_perms):
197 available_perms = config['available_permissions']
199 available_perms = config['available_permissions']
198 for perm in required_perms:
200 for perm in required_perms:
199 if perm not in available_perms:
201 if perm not in available_perms:
200 raise Exception("'%s' permission is not defined" % perm)
202 raise Exception("'%s' permission is not defined" % perm)
201 self.required_perms = set(required_perms)
203 self.required_perms = set(required_perms)
202 self.user_perms = None
204 self.user_perms = None
203
205
204 def __call__(self, func):
206 def __call__(self, func):
205 @wraps(func)
207 @wraps(func)
206 def _wrapper(*fargs, **fkwargs):
208 def _wrapper(*fargs, **fkwargs):
207 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
209 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
208 log.debug('checking %s permissions %s for %s',
210 log.debug('checking %s permissions %s for %s',
209 self.__class__.__name__, self.required_perms, func.__name__)
211 self.__class__.__name__, self.required_perms, func.__name__)
210
212
211 if self.check_permissions():
213 if self.check_permissions():
212 log.debug('Permission granted for %s', func.__name__)
214 log.debug('Permission granted for %s', func.__name__)
213 return func(*fargs)
215 return func(*fargs)
214
216
215 else:
217 else:
216 log.warning('Permission denied for %s', func.__name__)
218 log.warning('Permission denied for %s', func.__name__)
217 #redirect with forbidden ret code
219 #redirect with forbidden ret code
218 return abort(403)
220 return abort(403)
219 return _wrapper
221 return _wrapper
220
222
221
223
222 def check_permissions(self):
224 def check_permissions(self):
223 """
225 """
224 Dummy function for overriding
226 Dummy function for overriding
225 """
227 """
226 raise Exception('You have to write this function in child class')
228 raise Exception('You have to write this function in child class')
227
229
228 class HasPermissionAllDecorator(PermsDecorator):
230 class HasPermissionAllDecorator(PermsDecorator):
229 """
231 """
230 Checks for access permission for all given predicates. All of them have to
232 Checks for access permission for all given predicates. All of them have to
231 be meet in order to fulfill the request
233 be meet in order to fulfill the request
232 """
234 """
233
235
234 def check_permissions(self):
236 def check_permissions(self):
235 if self.required_perms.issubset(self.user_perms.get('global')):
237 if self.required_perms.issubset(self.user_perms.get('global')):
236 return True
238 return True
237 return False
239 return False
238
240
239
241
240 class HasPermissionAnyDecorator(PermsDecorator):
242 class HasPermissionAnyDecorator(PermsDecorator):
241 """
243 """
242 Checks for access permission for any of given predicates. In order to
244 Checks for access permission for any of given predicates. In order to
243 fulfill the request any of predicates must be meet
245 fulfill the request any of predicates must be meet
244 """
246 """
245
247
246 def check_permissions(self):
248 def check_permissions(self):
247 if self.required_perms.intersection(self.user_perms.get('global')):
249 if self.required_perms.intersection(self.user_perms.get('global')):
248 return True
250 return True
249 return False
251 return False
250
252
251 class HasRepoPermissionAllDecorator(PermsDecorator):
253 class HasRepoPermissionAllDecorator(PermsDecorator):
252 """
254 """
253 Checks for access permission for all given predicates for specific
255 Checks for access permission for all given predicates for specific
254 repository. All of them have to be meet in order to fulfill the request
256 repository. All of them have to be meet in order to fulfill the request
255 """
257 """
256
258
257 def check_permissions(self):
259 def check_permissions(self):
258 repo_name = get_repo_slug(request)
260 repo_name = get_repo_slug(request)
259 try:
261 try:
260 user_perms = set([self.user_perms['repositories'][repo_name]])
262 user_perms = set([self.user_perms['repositories'][repo_name]])
261 except KeyError:
263 except KeyError:
262 return False
264 return False
263 if self.required_perms.issubset(user_perms):
265 if self.required_perms.issubset(user_perms):
264 return True
266 return True
265 return False
267 return False
266
268
267
269
268 class HasRepoPermissionAnyDecorator(PermsDecorator):
270 class HasRepoPermissionAnyDecorator(PermsDecorator):
269 """
271 """
270 Checks for access permission for any of given predicates for specific
272 Checks for access permission for any of given predicates for specific
271 repository. In order to fulfill the request any of predicates must be meet
273 repository. In order to fulfill the request any of predicates must be meet
272 """
274 """
273
275
274 def check_permissions(self):
276 def check_permissions(self):
275 repo_name = get_repo_slug(request)
277 repo_name = get_repo_slug(request)
276
278
277 try:
279 try:
278 user_perms = set([self.user_perms['repositories'][repo_name]])
280 user_perms = set([self.user_perms['repositories'][repo_name]])
279 except KeyError:
281 except KeyError:
280 return False
282 return False
281 if self.required_perms.intersection(user_perms):
283 if self.required_perms.intersection(user_perms):
282 return True
284 return True
283 return False
285 return False
284 #===============================================================================
286 #===============================================================================
285 # CHECK FUNCTIONS
287 # CHECK FUNCTIONS
286 #===============================================================================
288 #===============================================================================
287
289
288 class PermsFunction(object):
290 class PermsFunction(object):
289 """
291 """
290 Base function for other check functions
292 Base function for other check functions
291 """
293 """
292
294
293 def __init__(self, *perms):
295 def __init__(self, *perms):
294 available_perms = config['available_permissions']
296 available_perms = config['available_permissions']
295
297
296 for perm in perms:
298 for perm in perms:
297 if perm not in available_perms:
299 if perm not in available_perms:
298 raise Exception("'%s' permission in not defined" % perm)
300 raise Exception("'%s' permission in not defined" % perm)
299 self.required_perms = set(perms)
301 self.required_perms = set(perms)
300 self.user_perms = None
302 self.user_perms = None
301 self.granted_for = ''
303 self.granted_for = ''
302 self.repo_name = None
304 self.repo_name = None
303
305
304 def __call__(self, check_Location=''):
306 def __call__(self, check_Location=''):
305 user = session.get('hg_app_user', False)
307 user = session.get('hg_app_user', False)
306 if not user:
308 if not user:
307 return False
309 return False
308 self.user_perms = user.permissions
310 self.user_perms = user.permissions
309 self.granted_for = user.username
311 self.granted_for = user.username
310 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
312 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
311
313
312 if self.check_permissions():
314 if self.check_permissions():
313 log.debug('Permission granted for %s @%s', self.granted_for,
315 log.debug('Permission granted for %s @%s', self.granted_for,
314 check_Location)
316 check_Location)
315 return True
317 return True
316
318
317 else:
319 else:
318 log.warning('Permission denied for %s @%s', self.granted_for,
320 log.warning('Permission denied for %s @%s', self.granted_for,
319 check_Location)
321 check_Location)
320 return False
322 return False
321
323
322 def check_permissions(self):
324 def check_permissions(self):
323 """
325 """
324 Dummy function for overriding
326 Dummy function for overriding
325 """
327 """
326 raise Exception('You have to write this function in child class')
328 raise Exception('You have to write this function in child class')
327
329
328 class HasPermissionAll(PermsFunction):
330 class HasPermissionAll(PermsFunction):
329 def check_permissions(self):
331 def check_permissions(self):
330 if self.required_perms.issubset(self.user_perms.get('global')):
332 if self.required_perms.issubset(self.user_perms.get('global')):
331 return True
333 return True
332 return False
334 return False
333
335
334 class HasPermissionAny(PermsFunction):
336 class HasPermissionAny(PermsFunction):
335 def check_permissions(self):
337 def check_permissions(self):
336 if self.required_perms.intersection(self.user_perms.get('global')):
338 if self.required_perms.intersection(self.user_perms.get('global')):
337 return True
339 return True
338 return False
340 return False
339
341
340 class HasRepoPermissionAll(PermsFunction):
342 class HasRepoPermissionAll(PermsFunction):
341
343
342 def __call__(self, repo_name=None, check_Location=''):
344 def __call__(self, repo_name=None, check_Location=''):
343 self.repo_name = repo_name
345 self.repo_name = repo_name
344 return super(HasRepoPermissionAll, self).__call__(check_Location)
346 return super(HasRepoPermissionAll, self).__call__(check_Location)
345
347
346 def check_permissions(self):
348 def check_permissions(self):
347 if not self.repo_name:
349 if not self.repo_name:
348 self.repo_name = get_repo_slug(request)
350 self.repo_name = get_repo_slug(request)
349
351
350 try:
352 try:
351 self.user_perms = set([self.user_perms['repositories']\
353 self.user_perms = set([self.user_perms['repositories']\
352 [self.repo_name]])
354 [self.repo_name]])
353 except KeyError:
355 except KeyError:
354 return False
356 return False
355 self.granted_for = self.repo_name
357 self.granted_for = self.repo_name
356 if self.required_perms.issubset(self.user_perms):
358 if self.required_perms.issubset(self.user_perms):
357 return True
359 return True
358 return False
360 return False
359
361
360 class HasRepoPermissionAny(PermsFunction):
362 class HasRepoPermissionAny(PermsFunction):
361
363
362
364
363 def __call__(self, repo_name=None, check_Location=''):
365 def __call__(self, repo_name=None, check_Location=''):
364 self.repo_name = repo_name
366 self.repo_name = repo_name
365 return super(HasRepoPermissionAny, self).__call__(check_Location)
367 return super(HasRepoPermissionAny, self).__call__(check_Location)
366
368
367 def check_permissions(self):
369 def check_permissions(self):
368 if not self.repo_name:
370 if not self.repo_name:
369 self.repo_name = get_repo_slug(request)
371 self.repo_name = get_repo_slug(request)
370
372
371 try:
373 try:
372 self.user_perms = set([self.user_perms['repositories']\
374 self.user_perms = set([self.user_perms['repositories']\
373 [self.repo_name]])
375 [self.repo_name]])
374 except KeyError:
376 except KeyError:
375 return False
377 return False
376 self.granted_for = self.repo_name
378 self.granted_for = self.repo_name
377 if self.required_perms.intersection(self.user_perms):
379 if self.required_perms.intersection(self.user_perms):
378 return True
380 return True
379 return False
381 return False
380
382
381 #===============================================================================
383 #===============================================================================
382 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
384 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
383 #===============================================================================
385 #===============================================================================
384
386
385 class HasPermissionAnyMiddleware(object):
387 class HasPermissionAnyMiddleware(object):
386 def __init__(self, *perms):
388 def __init__(self, *perms):
387 self.required_perms = set(perms)
389 self.required_perms = set(perms)
388
390
389 def __call__(self, user, repo_name):
391 def __call__(self, user, repo_name):
390 usr = AuthUser()
392 usr = AuthUser()
391 usr.user_id = user.user_id
393 usr.user_id = user.user_id
392 usr.username = user.username
394 usr.username = user.username
393 usr.is_admin = user.admin
395 usr.is_admin = user.admin
394
396
395 try:
397 try:
396 self.user_perms = set([fill_perms(usr)\
398 self.user_perms = set([fill_perms(usr)\
397 .permissions['repositories'][repo_name]])
399 .permissions['repositories'][repo_name]])
398 except:
400 except:
399 self.user_perms = set()
401 self.user_perms = set()
400 self.granted_for = ''
402 self.granted_for = ''
401 self.username = user.username
403 self.username = user.username
402 self.repo_name = repo_name
404 self.repo_name = repo_name
403 return self.check_permissions()
405 return self.check_permissions()
404
406
405 def check_permissions(self):
407 def check_permissions(self):
406 log.debug('checking mercurial protocol '
408 log.debug('checking mercurial protocol '
407 'permissions for user:%s repository:%s',
409 'permissions for user:%s repository:%s',
408 self.username, self.repo_name)
410 self.username, self.repo_name)
409 if self.required_perms.intersection(self.user_perms):
411 if self.required_perms.intersection(self.user_perms):
410 log.debug('permission granted')
412 log.debug('permission granted')
411 return True
413 return True
412 log.debug('permission denied')
414 log.debug('permission denied')
413 return False
415 return False
@@ -1,280 +1,282 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 from formencode import All
22 from formencode import All
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 Email, Bool, StringBoolean
24 Email, Bool, StringBoolean
25 from pylons import session
25 from pylons import session
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from pylons_app.lib.auth import get_crypt_password
27 from pylons_app.lib.auth import get_crypt_password
28 import pylons_app.lib.helpers as h
28 import pylons_app.lib.helpers as h
29 from pylons_app.model import meta
29 from pylons_app.model import meta
30 from pylons_app.model.db import User, Repository
30 from pylons_app.model.db import User, Repository
31 from sqlalchemy.exc import OperationalError
31 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34 import datetime
34 import datetime
35 import formencode
35 import formencode
36 import logging
36 import logging
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 #this is needed to translate the messages using _() in validators
40 #this is needed to translate the messages using _() in validators
41 class State_obj(object):
41 class State_obj(object):
42 _ = staticmethod(_)
42 _ = staticmethod(_)
43
43
44 #===============================================================================
44 #===============================================================================
45 # VALIDATORS
45 # VALIDATORS
46 #===============================================================================
46 #===============================================================================
47 class ValidAuthToken(formencode.validators.FancyValidator):
47 class ValidAuthToken(formencode.validators.FancyValidator):
48 messages = {'invalid_token':_('Token mismatch')}
48 messages = {'invalid_token':_('Token mismatch')}
49
49
50 def validate_python(self, value, state):
50 def validate_python(self, value, state):
51
51
52 if value != authentication_token():
52 if value != authentication_token():
53 raise formencode.Invalid(self.message('invalid_token', state,
53 raise formencode.Invalid(self.message('invalid_token', state,
54 search_number=value), value, state)
54 search_number=value), value, state)
55 class ValidUsername(formencode.validators.FancyValidator):
55 class ValidUsername(formencode.validators.FancyValidator):
56
56
57 def validate_python(self, value, state):
57 def validate_python(self, value, state):
58 if value in ['default', 'new_user']:
58 if value in ['default', 'new_user']:
59 raise formencode.Invalid(_('Invalid username'), value, state)
59 raise formencode.Invalid(_('Invalid username'), value, state)
60
60
61 class ValidPassword(formencode.validators.FancyValidator):
61 class ValidPassword(formencode.validators.FancyValidator):
62
62
63 def to_python(self, value, state):
63 def to_python(self, value, state):
64 if value:
64 if value:
65 return get_crypt_password(value)
65 return get_crypt_password(value)
66
66
67 class ValidAuth(formencode.validators.FancyValidator):
67 class ValidAuth(formencode.validators.FancyValidator):
68 messages = {
68 messages = {
69 'invalid_password':_('invalid password'),
69 'invalid_password':_('invalid password'),
70 'invalid_login':_('invalid user name'),
70 'invalid_login':_('invalid user name'),
71 'disabled_account':_('Your acccount is disabled')
71 'disabled_account':_('Your acccount is disabled')
72
72
73 }
73 }
74 #error mapping
74 #error mapping
75 e_dict = {'username':messages['invalid_login'],
75 e_dict = {'username':messages['invalid_login'],
76 'password':messages['invalid_password']}
76 'password':messages['invalid_password']}
77 e_dict_disable = {'username':messages['disabled_account']}
77 e_dict_disable = {'username':messages['disabled_account']}
78
78
79 def validate_python(self, value, state):
79 def validate_python(self, value, state):
80 sa = meta.Session
80 sa = meta.Session
81 crypted_passwd = get_crypt_password(value['password'])
81 crypted_passwd = get_crypt_password(value['password'])
82 username = value['username']
82 username = value['username']
83 try:
83 try:
84 user = sa.query(User).filter(User.username == username).one()
84 user = sa.query(User).filter(User.username == username).one()
85 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
85 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
86 log.error(e)
86 log.error(e)
87 user = None
87 user = None
88 raise formencode.Invalid(self.message('invalid_password',
88 raise formencode.Invalid(self.message('invalid_password',
89 state=State_obj), value, state,
89 state=State_obj), value, state,
90 error_dict=self.e_dict)
90 error_dict=self.e_dict)
91 if user:
91 if user:
92 if user.active:
92 if user.active:
93 if user.username == username and user.password == crypted_passwd:
93 if user.username == username and user.password == crypted_passwd:
94 from pylons_app.lib.auth import AuthUser
94 from pylons_app.lib.auth import AuthUser
95 auth_user = AuthUser()
95 auth_user = AuthUser()
96 auth_user.username = username
96 auth_user.username = username
97 auth_user.is_authenticated = True
97 auth_user.is_authenticated = True
98 auth_user.is_admin = user.admin
98 auth_user.is_admin = user.admin
99 auth_user.user_id = user.user_id
99 auth_user.user_id = user.user_id
100 auth_user.name = user.name
101 auth_user.lastname = user.lastname
100 session['hg_app_user'] = auth_user
102 session['hg_app_user'] = auth_user
101 session.save()
103 session.save()
102 log.info('user %s is now authenticated', username)
104 log.info('user %s is now authenticated', username)
103
105
104 try:
106 try:
105 user.last_login = datetime.datetime.now()
107 user.last_login = datetime.datetime.now()
106 sa.add(user)
108 sa.add(user)
107 sa.commit()
109 sa.commit()
108 except (OperationalError) as e:
110 except (OperationalError) as e:
109 log.error(e)
111 log.error(e)
110 sa.rollback()
112 sa.rollback()
111
113
112 return value
114 return value
113 else:
115 else:
114 log.warning('user %s not authenticated', username)
116 log.warning('user %s not authenticated', username)
115 raise formencode.Invalid(self.message('invalid_password',
117 raise formencode.Invalid(self.message('invalid_password',
116 state=State_obj), value, state,
118 state=State_obj), value, state,
117 error_dict=self.e_dict)
119 error_dict=self.e_dict)
118 else:
120 else:
119 log.warning('user %s is disabled', username)
121 log.warning('user %s is disabled', username)
120 raise formencode.Invalid(self.message('disabled_account',
122 raise formencode.Invalid(self.message('disabled_account',
121 state=State_obj),
123 state=State_obj),
122 value, state,
124 value, state,
123 error_dict=self.e_dict_disable)
125 error_dict=self.e_dict_disable)
124
126
125
127 meta.Session.remove()
126 class ValidRepoUser(formencode.validators.FancyValidator):
128 class ValidRepoUser(formencode.validators.FancyValidator):
127
129
128 def to_python(self, value, state):
130 def to_python(self, value, state):
129 sa = meta.Session
131 sa = meta.Session
130 try:
132 try:
131 self.user_db = sa.query(User)\
133 self.user_db = sa.query(User)\
132 .filter(User.active == True)\
134 .filter(User.active == True)\
133 .filter(User.username == value).one()
135 .filter(User.username == value).one()
134 except Exception:
136 except Exception:
135 raise formencode.Invalid(_('This username is not valid'),
137 raise formencode.Invalid(_('This username is not valid'),
136 value, state)
138 value, state)
137 return self.user_db.user_id
139 return self.user_db.user_id
138
140
139 def ValidRepoName(edit=False):
141 def ValidRepoName(edit=False):
140 class _ValidRepoName(formencode.validators.FancyValidator):
142 class _ValidRepoName(formencode.validators.FancyValidator):
141
143
142 def to_python(self, value, state):
144 def to_python(self, value, state):
143 slug = h.repo_name_slug(value)
145 slug = h.repo_name_slug(value)
144 if slug in ['_admin']:
146 if slug in ['_admin']:
145 raise formencode.Invalid(_('This repository name is disallowed'),
147 raise formencode.Invalid(_('This repository name is disallowed'),
146 value, state)
148 value, state)
147 sa = meta.Session
149 sa = meta.Session
148 if sa.query(Repository).get(slug) and not edit:
150 if sa.query(Repository).get(slug) and not edit:
149 raise formencode.Invalid(_('This repository already exists'),
151 raise formencode.Invalid(_('This repository already exists'),
150 value, state)
152 value, state)
151
153
152 return slug
154 return slug
153 return _ValidRepoName
155 return _ValidRepoName
154
156
155 class ValidPerms(formencode.validators.FancyValidator):
157 class ValidPerms(formencode.validators.FancyValidator):
156 messages = {'perm_new_user_name':_('This username is not valid')}
158 messages = {'perm_new_user_name':_('This username is not valid')}
157
159
158 def to_python(self, value, state):
160 def to_python(self, value, state):
159 perms_update = []
161 perms_update = []
160 perms_new = []
162 perms_new = []
161 #build a list of permission to update and new permission to create
163 #build a list of permission to update and new permission to create
162 for k, v in value.items():
164 for k, v in value.items():
163 if k.startswith('perm_'):
165 if k.startswith('perm_'):
164 if k.startswith('perm_new_user'):
166 if k.startswith('perm_new_user'):
165 new_perm = value.get('perm_new_user', False)
167 new_perm = value.get('perm_new_user', False)
166 new_user = value.get('perm_new_user_name', False)
168 new_user = value.get('perm_new_user_name', False)
167 if new_user and new_perm:
169 if new_user and new_perm:
168 if (new_user, new_perm) not in perms_new:
170 if (new_user, new_perm) not in perms_new:
169 perms_new.append((new_user, new_perm))
171 perms_new.append((new_user, new_perm))
170 else:
172 else:
171 usr = k[5:]
173 usr = k[5:]
172 if usr == 'default':
174 if usr == 'default':
173 if value['private']:
175 if value['private']:
174 #set none for default when updating to private repo
176 #set none for default when updating to private repo
175 v = 'repository.none'
177 v = 'repository.none'
176 perms_update.append((usr, v))
178 perms_update.append((usr, v))
177 value['perms_updates'] = perms_update
179 value['perms_updates'] = perms_update
178 value['perms_new'] = perms_new
180 value['perms_new'] = perms_new
179 sa = meta.Session
181 sa = meta.Session
180 for k, v in perms_new:
182 for k, v in perms_new:
181 try:
183 try:
182 self.user_db = sa.query(User)\
184 self.user_db = sa.query(User)\
183 .filter(User.active == True)\
185 .filter(User.active == True)\
184 .filter(User.username == k).one()
186 .filter(User.username == k).one()
185 except Exception:
187 except Exception:
186 msg = self.message('perm_new_user_name',
188 msg = self.message('perm_new_user_name',
187 state=State_obj)
189 state=State_obj)
188 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
190 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
189 return value
191 return value
190
192
191 class ValidSettings(formencode.validators.FancyValidator):
193 class ValidSettings(formencode.validators.FancyValidator):
192
194
193 def to_python(self, value, state):
195 def to_python(self, value, state):
194 #settings form can't edit user
196 #settings form can't edit user
195 if value.has_key('user'):
197 if value.has_key('user'):
196 del['value']['user']
198 del['value']['user']
197
199
198 return value
200 return value
199 #===============================================================================
201 #===============================================================================
200 # FORMS
202 # FORMS
201 #===============================================================================
203 #===============================================================================
202 class LoginForm(formencode.Schema):
204 class LoginForm(formencode.Schema):
203 allow_extra_fields = True
205 allow_extra_fields = True
204 filter_extra_fields = True
206 filter_extra_fields = True
205 username = UnicodeString(
207 username = UnicodeString(
206 strip=True,
208 strip=True,
207 min=3,
209 min=3,
208 not_empty=True,
210 not_empty=True,
209 messages={
211 messages={
210 'empty':_('Please enter a login'),
212 'empty':_('Please enter a login'),
211 'tooShort':_('Enter a value %(min)i characters long or more')}
213 'tooShort':_('Enter a value %(min)i characters long or more')}
212 )
214 )
213
215
214 password = UnicodeString(
216 password = UnicodeString(
215 strip=True,
217 strip=True,
216 min=3,
218 min=3,
217 not_empty=True,
219 not_empty=True,
218 messages={
220 messages={
219 'empty':_('Please enter a password'),
221 'empty':_('Please enter a password'),
220 'tooShort':_('Enter a value %(min)i characters long or more')}
222 'tooShort':_('Enter a value %(min)i characters long or more')}
221 )
223 )
222
224
223
225
224 #chained validators have access to all data
226 #chained validators have access to all data
225 chained_validators = [ValidAuth]
227 chained_validators = [ValidAuth]
226
228
227 def UserForm(edit=False):
229 def UserForm(edit=False):
228 class _UserForm(formencode.Schema):
230 class _UserForm(formencode.Schema):
229 allow_extra_fields = True
231 allow_extra_fields = True
230 filter_extra_fields = True
232 filter_extra_fields = True
231 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername)
233 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername)
232 if edit:
234 if edit:
233 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
235 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
234 admin = StringBoolean(if_missing=False)
236 admin = StringBoolean(if_missing=False)
235 else:
237 else:
236 password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
238 password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
237 active = StringBoolean(if_missing=False)
239 active = StringBoolean(if_missing=False)
238 name = UnicodeString(strip=True, min=3, not_empty=True)
240 name = UnicodeString(strip=True, min=3, not_empty=True)
239 lastname = UnicodeString(strip=True, min=3, not_empty=True)
241 lastname = UnicodeString(strip=True, min=3, not_empty=True)
240 email = Email(not_empty=True)
242 email = Email(not_empty=True)
241
243
242 return _UserForm
244 return _UserForm
243
245
244 def RepoForm(edit=False):
246 def RepoForm(edit=False):
245 class _RepoForm(formencode.Schema):
247 class _RepoForm(formencode.Schema):
246 allow_extra_fields = True
248 allow_extra_fields = True
247 filter_extra_fields = False
249 filter_extra_fields = False
248 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit))
250 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit))
249 description = UnicodeString(strip=True, min=3, not_empty=True)
251 description = UnicodeString(strip=True, min=3, not_empty=True)
250 private = StringBoolean(if_missing=False)
252 private = StringBoolean(if_missing=False)
251
253
252 if edit:
254 if edit:
253 user = All(Int(not_empty=True), ValidRepoUser)
255 user = All(Int(not_empty=True), ValidRepoUser)
254
256
255 chained_validators = [ValidPerms]
257 chained_validators = [ValidPerms]
256 return _RepoForm
258 return _RepoForm
257
259
258 def RepoSettingsForm(edit=False):
260 def RepoSettingsForm(edit=False):
259 class _RepoForm(formencode.Schema):
261 class _RepoForm(formencode.Schema):
260 allow_extra_fields = True
262 allow_extra_fields = True
261 filter_extra_fields = False
263 filter_extra_fields = False
262 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit))
264 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit))
263 description = UnicodeString(strip=True, min=3, not_empty=True)
265 description = UnicodeString(strip=True, min=3, not_empty=True)
264 private = StringBoolean(if_missing=False)
266 private = StringBoolean(if_missing=False)
265
267
266 chained_validators = [ValidPerms, ValidSettings]
268 chained_validators = [ValidPerms, ValidSettings]
267 return _RepoForm
269 return _RepoForm
268
270
269
271
270 def ApplicationSettingsForm():
272 def ApplicationSettingsForm():
271 class _ApplicationSettingsForm(formencode.Schema):
273 class _ApplicationSettingsForm(formencode.Schema):
272 allow_extra_fields = True
274 allow_extra_fields = True
273 filter_extra_fields = False
275 filter_extra_fields = False
274 app_title = UnicodeString(strip=True, min=3, not_empty=True)
276 app_title = UnicodeString(strip=True, min=3, not_empty=True)
275 app_auth_realm = UnicodeString(strip=True, min=3, not_empty=True)
277 app_auth_realm = UnicodeString(strip=True, min=3, not_empty=True)
276
278
277 return _ApplicationSettingsForm
279 return _ApplicationSettingsForm
278
280
279
281
280
282
General Comments 0
You need to be logged in to leave comments. Login now