##// END OF EJS Templates
Changed password crypting scheme to bcrypt, added dependency for setup
marcink -
r415:04e8b31f default
parent child Browse files
Show More
@@ -1,432 +1,433
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 pylons import config, session, url, request
26 from pylons import config, session, url, request
27 from pylons.controllers.util import abort, redirect
27 from pylons.controllers.util import abort, redirect
28 from pylons_app.lib.utils import get_repo_slug
28 from pylons_app.lib.utils import get_repo_slug
29 from pylons_app.model import meta
29 from pylons_app.model import meta
30 from pylons_app.model.db import User, RepoToPerm, Repository, Permission
30 from pylons_app.model.db import User, RepoToPerm, Repository, Permission
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 import hashlib
33 import bcrypt
34 from decorator import decorator
34 from decorator import decorator
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 """Cryptographic function used for password hashing based on sha1
40 """Cryptographic function used for password hashing based on sha1
41 @param password: password to hash
41 @param password: password to hash
42 """
42 """
43 hashed = hashlib.sha1(password).hexdigest()
43 return bcrypt.hashpw(password, bcrypt.gensalt(10))
44 return hashed[3:] + hashed[:3]
44
45 def check_password(password, hashed):
46 return bcrypt.hashpw(password, hashed) == hashed
45
47
46 @cache_region('super_short_term', 'cached_user')
48 @cache_region('super_short_term', 'cached_user')
47 def get_user_cached(username):
49 def get_user_cached(username):
48 sa = meta.Session
50 sa = meta.Session
49 try:
51 try:
50 user = sa.query(User).filter(User.username == username).one()
52 user = sa.query(User).filter(User.username == username).one()
51 finally:
53 finally:
52 meta.Session.remove()
54 meta.Session.remove()
53 return user
55 return user
54
56
55 def authfunc(environ, username, password):
57 def authfunc(environ, username, password):
56 password_crypt = get_crypt_password(password)
57 try:
58 try:
58 user = get_user_cached(username)
59 user = get_user_cached(username)
59 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
60 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
60 log.error(e)
61 log.error(e)
61 user = None
62 user = None
62
63
63 if user:
64 if user:
64 if user.active:
65 if user.active:
65 if user.username == username and user.password == password_crypt:
66 if user.username == username and check_password(password, user.password):
66 log.info('user %s authenticated correctly', username)
67 log.info('user %s authenticated correctly', username)
67 return True
68 return True
68 else:
69 else:
69 log.error('user %s is disabled', username)
70 log.error('user %s is disabled', username)
70
71
71 return False
72 return False
72
73
73 class AuthUser(object):
74 class AuthUser(object):
74 """
75 """
75 A simple object that handles a mercurial username for authentication
76 A simple object that handles a mercurial username for authentication
76 """
77 """
77 def __init__(self):
78 def __init__(self):
78 self.username = 'None'
79 self.username = 'None'
79 self.name = ''
80 self.name = ''
80 self.lastname = ''
81 self.lastname = ''
81 self.email = ''
82 self.email = ''
82 self.user_id = None
83 self.user_id = None
83 self.is_authenticated = False
84 self.is_authenticated = False
84 self.is_admin = False
85 self.is_admin = False
85 self.permissions = {}
86 self.permissions = {}
86
87
87
88
88 def set_available_permissions(config):
89 def set_available_permissions(config):
89 """
90 """
90 This function will propagate pylons globals with all available defined
91 This function will propagate pylons globals with all available defined
91 permission given in db. We don't wannt to check each time from db for new
92 permission given in db. We don't wannt to check each time from db for new
92 permissions since adding a new permission also requires application restart
93 permissions since adding a new permission also requires application restart
93 ie. to decorate new views with the newly created permission
94 ie. to decorate new views with the newly created permission
94 @param config:
95 @param config:
95 """
96 """
96 log.info('getting information about all available permissions')
97 log.info('getting information about all available permissions')
97 try:
98 try:
98 sa = meta.Session
99 sa = meta.Session
99 all_perms = sa.query(Permission).all()
100 all_perms = sa.query(Permission).all()
100 finally:
101 finally:
101 meta.Session.remove()
102 meta.Session.remove()
102
103
103 config['available_permissions'] = [x.permission_name for x in all_perms]
104 config['available_permissions'] = [x.permission_name for x in all_perms]
104
105
105 def set_base_path(config):
106 def set_base_path(config):
106 config['base_path'] = config['pylons.app_globals'].base_path
107 config['base_path'] = config['pylons.app_globals'].base_path
107
108
108 def fill_data(user):
109 def fill_data(user):
109 """
110 """
110 Fills user data with those from database and log out user if not present
111 Fills user data with those from database and log out user if not present
111 in database
112 in database
112 @param user:
113 @param user:
113 """
114 """
114 sa = meta.Session
115 sa = meta.Session
115 dbuser = sa.query(User).get(user.user_id)
116 dbuser = sa.query(User).get(user.user_id)
116 if dbuser:
117 if dbuser:
117 user.username = dbuser.username
118 user.username = dbuser.username
118 user.is_admin = dbuser.admin
119 user.is_admin = dbuser.admin
119 user.name = dbuser.name
120 user.name = dbuser.name
120 user.lastname = dbuser.lastname
121 user.lastname = dbuser.lastname
121 user.email = dbuser.email
122 user.email = dbuser.email
122 else:
123 else:
123 user.is_authenticated = False
124 user.is_authenticated = False
124 meta.Session.remove()
125 meta.Session.remove()
125 return user
126 return user
126
127
127 def fill_perms(user):
128 def fill_perms(user):
128 """
129 """
129 Fills user permission attribute with permissions taken from database
130 Fills user permission attribute with permissions taken from database
130 @param user:
131 @param user:
131 """
132 """
132
133
133 sa = meta.Session
134 sa = meta.Session
134 user.permissions['repositories'] = {}
135 user.permissions['repositories'] = {}
135 user.permissions['global'] = set()
136 user.permissions['global'] = set()
136
137
137 #first fetch default permissions
138 #first fetch default permissions
138 default_perms = sa.query(RepoToPerm, Repository, Permission)\
139 default_perms = sa.query(RepoToPerm, Repository, Permission)\
139 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
140 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
140 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
141 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
141 .filter(RepoToPerm.user_id == sa.query(User).filter(User.username ==
142 .filter(RepoToPerm.user_id == sa.query(User).filter(User.username ==
142 'default').one().user_id).all()
143 'default').one().user_id).all()
143
144
144 if user.is_admin:
145 if user.is_admin:
145 user.permissions['global'].add('hg.admin')
146 user.permissions['global'].add('hg.admin')
146 #admin have all rights set to admin
147 #admin have all rights set to admin
147 for perm in default_perms:
148 for perm in default_perms:
148 p = 'repository.admin'
149 p = 'repository.admin'
149 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
150 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
150
151
151 else:
152 else:
152 user.permissions['global'].add('repository.create')
153 user.permissions['global'].add('repository.create')
153 user.permissions['global'].add('hg.register')
154 user.permissions['global'].add('hg.register')
154
155
155 for perm in default_perms:
156 for perm in default_perms:
156 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
157 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
157 #disable defaults for private repos,
158 #disable defaults for private repos,
158 p = 'repository.none'
159 p = 'repository.none'
159 elif perm.Repository.user_id == user.user_id:
160 elif perm.Repository.user_id == user.user_id:
160 #set admin if owner
161 #set admin if owner
161 p = 'repository.admin'
162 p = 'repository.admin'
162 else:
163 else:
163 p = perm.Permission.permission_name
164 p = perm.Permission.permission_name
164
165
165 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
166 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
166
167
167
168
168 user_perms = sa.query(RepoToPerm, Permission, Repository)\
169 user_perms = sa.query(RepoToPerm, Permission, Repository)\
169 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
170 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
171 .filter(RepoToPerm.user_id == user.user_id).all()
172 .filter(RepoToPerm.user_id == user.user_id).all()
172 #overwrite userpermissions with defaults
173 #overwrite userpermissions with defaults
173 for perm in user_perms:
174 for perm in user_perms:
174 #set write if owner
175 #set write if owner
175 if perm.Repository.user_id == user.user_id:
176 if perm.Repository.user_id == user.user_id:
176 p = 'repository.write'
177 p = 'repository.write'
177 else:
178 else:
178 p = perm.Permission.permission_name
179 p = perm.Permission.permission_name
179 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
180 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
180 meta.Session.remove()
181 meta.Session.remove()
181 return user
182 return user
182
183
183 def get_user(session):
184 def get_user(session):
184 """
185 """
185 Gets user from session, and wraps permissions into user
186 Gets user from session, and wraps permissions into user
186 @param session:
187 @param session:
187 """
188 """
188 user = session.get('hg_app_user', AuthUser())
189 user = session.get('hg_app_user', AuthUser())
189 if user.is_authenticated:
190 if user.is_authenticated:
190 user = fill_data(user)
191 user = fill_data(user)
191 user = fill_perms(user)
192 user = fill_perms(user)
192 session['hg_app_user'] = user
193 session['hg_app_user'] = user
193 session.save()
194 session.save()
194 return user
195 return user
195
196
196 #===============================================================================
197 #===============================================================================
197 # CHECK DECORATORS
198 # CHECK DECORATORS
198 #===============================================================================
199 #===============================================================================
199 class LoginRequired(object):
200 class LoginRequired(object):
200 """Must be logged in to execute this function else redirect to login page"""
201 """Must be logged in to execute this function else redirect to login page"""
201
202
202 def __call__(self, func):
203 def __call__(self, func):
203 return decorator(self.__wrapper, func)
204 return decorator(self.__wrapper, func)
204
205
205 def __wrapper(self, func, *fargs, **fkwargs):
206 def __wrapper(self, func, *fargs, **fkwargs):
206 user = session.get('hg_app_user', AuthUser())
207 user = session.get('hg_app_user', AuthUser())
207 log.debug('Checking login required for user:%s', user.username)
208 log.debug('Checking login required for user:%s', user.username)
208 if user.is_authenticated:
209 if user.is_authenticated:
209 log.debug('user %s is authenticated', user.username)
210 log.debug('user %s is authenticated', user.username)
210 return func(*fargs, **fkwargs)
211 return func(*fargs, **fkwargs)
211 else:
212 else:
212 log.warn('user %s not authenticated', user.username)
213 log.warn('user %s not authenticated', user.username)
213 log.debug('redirecting to login page')
214 log.debug('redirecting to login page')
214 return redirect(url('login_home'))
215 return redirect(url('login_home'))
215
216
216 class PermsDecorator(object):
217 class PermsDecorator(object):
217 """Base class for decorators"""
218 """Base class for decorators"""
218
219
219 def __init__(self, *required_perms):
220 def __init__(self, *required_perms):
220 available_perms = config['available_permissions']
221 available_perms = config['available_permissions']
221 for perm in required_perms:
222 for perm in required_perms:
222 if perm not in available_perms:
223 if perm not in available_perms:
223 raise Exception("'%s' permission is not defined" % perm)
224 raise Exception("'%s' permission is not defined" % perm)
224 self.required_perms = set(required_perms)
225 self.required_perms = set(required_perms)
225 self.user_perms = None
226 self.user_perms = None
226
227
227 def __call__(self, func):
228 def __call__(self, func):
228 return decorator(self.__wrapper, func)
229 return decorator(self.__wrapper, func)
229
230
230
231
231 def __wrapper(self, func, *fargs, **fkwargs):
232 def __wrapper(self, func, *fargs, **fkwargs):
232 # _wrapper.__name__ = func.__name__
233 # _wrapper.__name__ = func.__name__
233 # _wrapper.__dict__.update(func.__dict__)
234 # _wrapper.__dict__.update(func.__dict__)
234 # _wrapper.__doc__ = func.__doc__
235 # _wrapper.__doc__ = func.__doc__
235
236
236 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
237 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
237 log.debug('checking %s permissions %s for %s',
238 log.debug('checking %s permissions %s for %s',
238 self.__class__.__name__, self.required_perms, func.__name__)
239 self.__class__.__name__, self.required_perms, func.__name__)
239
240
240 if self.check_permissions():
241 if self.check_permissions():
241 log.debug('Permission granted for %s', func.__name__)
242 log.debug('Permission granted for %s', func.__name__)
242
243
243 return func(*fargs, **fkwargs)
244 return func(*fargs, **fkwargs)
244
245
245 else:
246 else:
246 log.warning('Permission denied for %s', func.__name__)
247 log.warning('Permission denied for %s', func.__name__)
247 #redirect with forbidden ret code
248 #redirect with forbidden ret code
248 return abort(403)
249 return abort(403)
249
250
250
251
251
252
252 def check_permissions(self):
253 def check_permissions(self):
253 """Dummy function for overriding"""
254 """Dummy function for overriding"""
254 raise Exception('You have to write this function in child class')
255 raise Exception('You have to write this function in child class')
255
256
256 class HasPermissionAllDecorator(PermsDecorator):
257 class HasPermissionAllDecorator(PermsDecorator):
257 """Checks for access permission for all given predicates. All of them
258 """Checks for access permission for all given predicates. All of them
258 have to be meet in order to fulfill the request
259 have to be meet in order to fulfill the request
259 """
260 """
260
261
261 def check_permissions(self):
262 def check_permissions(self):
262 if self.required_perms.issubset(self.user_perms.get('global')):
263 if self.required_perms.issubset(self.user_perms.get('global')):
263 return True
264 return True
264 return False
265 return False
265
266
266
267
267 class HasPermissionAnyDecorator(PermsDecorator):
268 class HasPermissionAnyDecorator(PermsDecorator):
268 """Checks for access permission for any of given predicates. In order to
269 """Checks for access permission for any of given predicates. In order to
269 fulfill the request any of predicates must be meet
270 fulfill the request any of predicates must be meet
270 """
271 """
271
272
272 def check_permissions(self):
273 def check_permissions(self):
273 if self.required_perms.intersection(self.user_perms.get('global')):
274 if self.required_perms.intersection(self.user_perms.get('global')):
274 return True
275 return True
275 return False
276 return False
276
277
277 class HasRepoPermissionAllDecorator(PermsDecorator):
278 class HasRepoPermissionAllDecorator(PermsDecorator):
278 """Checks for access permission for all given predicates for specific
279 """Checks for access permission for all given predicates for specific
279 repository. All of them have to be meet in order to fulfill the request
280 repository. All of them have to be meet in order to fulfill the request
280 """
281 """
281
282
282 def check_permissions(self):
283 def check_permissions(self):
283 repo_name = get_repo_slug(request)
284 repo_name = get_repo_slug(request)
284 try:
285 try:
285 user_perms = set([self.user_perms['repositories'][repo_name]])
286 user_perms = set([self.user_perms['repositories'][repo_name]])
286 except KeyError:
287 except KeyError:
287 return False
288 return False
288 if self.required_perms.issubset(user_perms):
289 if self.required_perms.issubset(user_perms):
289 return True
290 return True
290 return False
291 return False
291
292
292
293
293 class HasRepoPermissionAnyDecorator(PermsDecorator):
294 class HasRepoPermissionAnyDecorator(PermsDecorator):
294 """Checks for access permission for any of given predicates for specific
295 """Checks for access permission for any of given predicates for specific
295 repository. In order to fulfill the request any of predicates must be meet
296 repository. In order to fulfill the request any of predicates must be meet
296 """
297 """
297
298
298 def check_permissions(self):
299 def check_permissions(self):
299 repo_name = get_repo_slug(request)
300 repo_name = get_repo_slug(request)
300
301
301 try:
302 try:
302 user_perms = set([self.user_perms['repositories'][repo_name]])
303 user_perms = set([self.user_perms['repositories'][repo_name]])
303 except KeyError:
304 except KeyError:
304 return False
305 return False
305 if self.required_perms.intersection(user_perms):
306 if self.required_perms.intersection(user_perms):
306 return True
307 return True
307 return False
308 return False
308 #===============================================================================
309 #===============================================================================
309 # CHECK FUNCTIONS
310 # CHECK FUNCTIONS
310 #===============================================================================
311 #===============================================================================
311
312
312 class PermsFunction(object):
313 class PermsFunction(object):
313 """Base function for other check functions"""
314 """Base function for other check functions"""
314
315
315 def __init__(self, *perms):
316 def __init__(self, *perms):
316 available_perms = config['available_permissions']
317 available_perms = config['available_permissions']
317
318
318 for perm in perms:
319 for perm in perms:
319 if perm not in available_perms:
320 if perm not in available_perms:
320 raise Exception("'%s' permission in not defined" % perm)
321 raise Exception("'%s' permission in not defined" % perm)
321 self.required_perms = set(perms)
322 self.required_perms = set(perms)
322 self.user_perms = None
323 self.user_perms = None
323 self.granted_for = ''
324 self.granted_for = ''
324 self.repo_name = None
325 self.repo_name = None
325
326
326 def __call__(self, check_Location=''):
327 def __call__(self, check_Location=''):
327 user = session.get('hg_app_user', False)
328 user = session.get('hg_app_user', False)
328 if not user:
329 if not user:
329 return False
330 return False
330 self.user_perms = user.permissions
331 self.user_perms = user.permissions
331 self.granted_for = user.username
332 self.granted_for = user.username
332 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
333 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
333
334
334 if self.check_permissions():
335 if self.check_permissions():
335 log.debug('Permission granted for %s @%s', self.granted_for,
336 log.debug('Permission granted for %s @%s', self.granted_for,
336 check_Location)
337 check_Location)
337 return True
338 return True
338
339
339 else:
340 else:
340 log.warning('Permission denied for %s @%s', self.granted_for,
341 log.warning('Permission denied for %s @%s', self.granted_for,
341 check_Location)
342 check_Location)
342 return False
343 return False
343
344
344 def check_permissions(self):
345 def check_permissions(self):
345 """Dummy function for overriding"""
346 """Dummy function for overriding"""
346 raise Exception('You have to write this function in child class')
347 raise Exception('You have to write this function in child class')
347
348
348 class HasPermissionAll(PermsFunction):
349 class HasPermissionAll(PermsFunction):
349 def check_permissions(self):
350 def check_permissions(self):
350 if self.required_perms.issubset(self.user_perms.get('global')):
351 if self.required_perms.issubset(self.user_perms.get('global')):
351 return True
352 return True
352 return False
353 return False
353
354
354 class HasPermissionAny(PermsFunction):
355 class HasPermissionAny(PermsFunction):
355 def check_permissions(self):
356 def check_permissions(self):
356 if self.required_perms.intersection(self.user_perms.get('global')):
357 if self.required_perms.intersection(self.user_perms.get('global')):
357 return True
358 return True
358 return False
359 return False
359
360
360 class HasRepoPermissionAll(PermsFunction):
361 class HasRepoPermissionAll(PermsFunction):
361
362
362 def __call__(self, repo_name=None, check_Location=''):
363 def __call__(self, repo_name=None, check_Location=''):
363 self.repo_name = repo_name
364 self.repo_name = repo_name
364 return super(HasRepoPermissionAll, self).__call__(check_Location)
365 return super(HasRepoPermissionAll, self).__call__(check_Location)
365
366
366 def check_permissions(self):
367 def check_permissions(self):
367 if not self.repo_name:
368 if not self.repo_name:
368 self.repo_name = get_repo_slug(request)
369 self.repo_name = get_repo_slug(request)
369
370
370 try:
371 try:
371 self.user_perms = set([self.user_perms['repositories']\
372 self.user_perms = set([self.user_perms['repositories']\
372 [self.repo_name]])
373 [self.repo_name]])
373 except KeyError:
374 except KeyError:
374 return False
375 return False
375 self.granted_for = self.repo_name
376 self.granted_for = self.repo_name
376 if self.required_perms.issubset(self.user_perms):
377 if self.required_perms.issubset(self.user_perms):
377 return True
378 return True
378 return False
379 return False
379
380
380 class HasRepoPermissionAny(PermsFunction):
381 class HasRepoPermissionAny(PermsFunction):
381
382
382 def __call__(self, repo_name=None, check_Location=''):
383 def __call__(self, repo_name=None, check_Location=''):
383 self.repo_name = repo_name
384 self.repo_name = repo_name
384 return super(HasRepoPermissionAny, self).__call__(check_Location)
385 return super(HasRepoPermissionAny, self).__call__(check_Location)
385
386
386 def check_permissions(self):
387 def check_permissions(self):
387 if not self.repo_name:
388 if not self.repo_name:
388 self.repo_name = get_repo_slug(request)
389 self.repo_name = get_repo_slug(request)
389
390
390 try:
391 try:
391 self.user_perms = set([self.user_perms['repositories']\
392 self.user_perms = set([self.user_perms['repositories']\
392 [self.repo_name]])
393 [self.repo_name]])
393 except KeyError:
394 except KeyError:
394 return False
395 return False
395 self.granted_for = self.repo_name
396 self.granted_for = self.repo_name
396 if self.required_perms.intersection(self.user_perms):
397 if self.required_perms.intersection(self.user_perms):
397 return True
398 return True
398 return False
399 return False
399
400
400 #===============================================================================
401 #===============================================================================
401 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
402 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
402 #===============================================================================
403 #===============================================================================
403
404
404 class HasPermissionAnyMiddleware(object):
405 class HasPermissionAnyMiddleware(object):
405 def __init__(self, *perms):
406 def __init__(self, *perms):
406 self.required_perms = set(perms)
407 self.required_perms = set(perms)
407
408
408 def __call__(self, user, repo_name):
409 def __call__(self, user, repo_name):
409 usr = AuthUser()
410 usr = AuthUser()
410 usr.user_id = user.user_id
411 usr.user_id = user.user_id
411 usr.username = user.username
412 usr.username = user.username
412 usr.is_admin = user.admin
413 usr.is_admin = user.admin
413
414
414 try:
415 try:
415 self.user_perms = set([fill_perms(usr)\
416 self.user_perms = set([fill_perms(usr)\
416 .permissions['repositories'][repo_name]])
417 .permissions['repositories'][repo_name]])
417 except:
418 except:
418 self.user_perms = set()
419 self.user_perms = set()
419 self.granted_for = ''
420 self.granted_for = ''
420 self.username = user.username
421 self.username = user.username
421 self.repo_name = repo_name
422 self.repo_name = repo_name
422 return self.check_permissions()
423 return self.check_permissions()
423
424
424 def check_permissions(self):
425 def check_permissions(self):
425 log.debug('checking mercurial protocol '
426 log.debug('checking mercurial protocol '
426 'permissions for user:%s repository:%s',
427 'permissions for user:%s repository:%s',
427 self.username, self.repo_name)
428 self.username, self.repo_name)
428 if self.required_perms.intersection(self.user_perms):
429 if self.required_perms.intersection(self.user_perms):
429 log.debug('permission granted')
430 log.debug('permission granted')
430 return True
431 return True
431 log.debug('permission denied')
432 log.debug('permission denied')
432 return False
433 return False
@@ -1,330 +1,330
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 check_password
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.db import User, Repository
29 from pylons_app.model.db import User, Repository
30 from sqlalchemy.exc import OperationalError
30 from sqlalchemy.exc import OperationalError
31 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
31 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
32 from webhelpers.pylonslib.secure_form import authentication_token
32 from webhelpers.pylonslib.secure_form import authentication_token
33 import datetime
33 import datetime
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import os
36 import os
37 import pylons_app.lib.helpers as h
37 import pylons_app.lib.helpers as h
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 #this is needed to translate the messages using _() in validators
41 #this is needed to translate the messages using _() in validators
42 class State_obj(object):
42 class State_obj(object):
43 _ = staticmethod(_)
43 _ = staticmethod(_)
44
44
45 #===============================================================================
45 #===============================================================================
46 # VALIDATORS
46 # VALIDATORS
47 #===============================================================================
47 #===============================================================================
48 class ValidAuthToken(formencode.validators.FancyValidator):
48 class ValidAuthToken(formencode.validators.FancyValidator):
49 messages = {'invalid_token':_('Token mismatch')}
49 messages = {'invalid_token':_('Token mismatch')}
50
50
51 def validate_python(self, value, state):
51 def validate_python(self, value, state):
52
52
53 if value != authentication_token():
53 if value != authentication_token():
54 raise formencode.Invalid(self.message('invalid_token', state,
54 raise formencode.Invalid(self.message('invalid_token', state,
55 search_number=value), value, state)
55 search_number=value), value, state)
56
56
57 def ValidUsername(edit, old_data):
57 def ValidUsername(edit, old_data):
58 class _ValidUsername(formencode.validators.FancyValidator):
58 class _ValidUsername(formencode.validators.FancyValidator):
59
59
60 def validate_python(self, value, state):
60 def validate_python(self, value, state):
61 if value in ['default', 'new_user']:
61 if value in ['default', 'new_user']:
62 raise formencode.Invalid(_('Invalid username'), value, state)
62 raise formencode.Invalid(_('Invalid username'), value, state)
63 #check if user is uniq
63 #check if user is uniq
64 sa = meta.Session
64 sa = meta.Session
65 old_un = None
65 old_un = None
66 if edit:
66 if edit:
67 old_un = sa.query(User).get(old_data.get('user_id')).username
67 old_un = sa.query(User).get(old_data.get('user_id')).username
68
68
69 if old_un != value or not edit:
69 if old_un != value or not edit:
70 if sa.query(User).filter(User.username == value).scalar():
70 if sa.query(User).filter(User.username == value).scalar():
71 raise formencode.Invalid(_('This username already exists') ,
71 raise formencode.Invalid(_('This username already exists') ,
72 value, state)
72 value, state)
73 meta.Session.remove()
73 meta.Session.remove()
74
74
75 return _ValidUsername
75 return _ValidUsername
76
76
77 class ValidPassword(formencode.validators.FancyValidator):
77 class ValidPassword(formencode.validators.FancyValidator):
78
78
79 def to_python(self, value, state):
79 def to_python(self, value, state):
80 if value:
80 if value:
81 return get_crypt_password(value)
81 return get_crypt_password(value)
82
82
83 class ValidAuth(formencode.validators.FancyValidator):
83 class ValidAuth(formencode.validators.FancyValidator):
84 messages = {
84 messages = {
85 'invalid_password':_('invalid password'),
85 'invalid_password':_('invalid password'),
86 'invalid_login':_('invalid user name'),
86 'invalid_login':_('invalid user name'),
87 'disabled_account':_('Your acccount is disabled')
87 'disabled_account':_('Your acccount is disabled')
88
88
89 }
89 }
90 #error mapping
90 #error mapping
91 e_dict = {'username':messages['invalid_login'],
91 e_dict = {'username':messages['invalid_login'],
92 'password':messages['invalid_password']}
92 'password':messages['invalid_password']}
93 e_dict_disable = {'username':messages['disabled_account']}
93 e_dict_disable = {'username':messages['disabled_account']}
94
94
95 def validate_python(self, value, state):
95 def validate_python(self, value, state):
96 sa = meta.Session
96 sa = meta.Session
97 crypted_passwd = get_crypt_password(value['password'])
97 password = value['password']
98 username = value['username']
98 username = value['username']
99 try:
99 try:
100 user = sa.query(User).filter(User.username == username).one()
100 user = sa.query(User).filter(User.username == username).one()
101 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
101 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
102 log.error(e)
102 log.error(e)
103 user = None
103 user = None
104 raise formencode.Invalid(self.message('invalid_password',
104 raise formencode.Invalid(self.message('invalid_password',
105 state=State_obj), value, state,
105 state=State_obj), value, state,
106 error_dict=self.e_dict)
106 error_dict=self.e_dict)
107 if user:
107 if user:
108 if user.active:
108 if user.active:
109 if user.username == username and user.password == crypted_passwd:
109 if user.username == username and check_password(password, user.password):
110 from pylons_app.lib.auth import AuthUser
110 from pylons_app.lib.auth import AuthUser
111 auth_user = AuthUser()
111 auth_user = AuthUser()
112 auth_user.username = username
112 auth_user.username = username
113 auth_user.is_authenticated = True
113 auth_user.is_authenticated = True
114 auth_user.is_admin = user.admin
114 auth_user.is_admin = user.admin
115 auth_user.user_id = user.user_id
115 auth_user.user_id = user.user_id
116 auth_user.name = user.name
116 auth_user.name = user.name
117 auth_user.lastname = user.lastname
117 auth_user.lastname = user.lastname
118 session['hg_app_user'] = auth_user
118 session['hg_app_user'] = auth_user
119 session.save()
119 session.save()
120 log.info('user %s is now authenticated', username)
120 log.info('user %s is now authenticated', username)
121
121
122 try:
122 try:
123 user.last_login = datetime.datetime.now()
123 user.last_login = datetime.datetime.now()
124 sa.add(user)
124 sa.add(user)
125 sa.commit()
125 sa.commit()
126 except (OperationalError) as e:
126 except (OperationalError) as e:
127 log.error(e)
127 log.error(e)
128 sa.rollback()
128 sa.rollback()
129
129
130 return value
130 return value
131 else:
131 else:
132 log.warning('user %s not authenticated', username)
132 log.warning('user %s not authenticated', username)
133 raise formencode.Invalid(self.message('invalid_password',
133 raise formencode.Invalid(self.message('invalid_password',
134 state=State_obj), value, state,
134 state=State_obj), value, state,
135 error_dict=self.e_dict)
135 error_dict=self.e_dict)
136 else:
136 else:
137 log.warning('user %s is disabled', username)
137 log.warning('user %s is disabled', username)
138 raise formencode.Invalid(self.message('disabled_account',
138 raise formencode.Invalid(self.message('disabled_account',
139 state=State_obj),
139 state=State_obj),
140 value, state,
140 value, state,
141 error_dict=self.e_dict_disable)
141 error_dict=self.e_dict_disable)
142
142
143 meta.Session.remove()
143 meta.Session.remove()
144
144
145
145
146 class ValidRepoUser(formencode.validators.FancyValidator):
146 class ValidRepoUser(formencode.validators.FancyValidator):
147
147
148 def to_python(self, value, state):
148 def to_python(self, value, state):
149 sa = meta.Session
149 sa = meta.Session
150 try:
150 try:
151 self.user_db = sa.query(User)\
151 self.user_db = sa.query(User)\
152 .filter(User.active == True)\
152 .filter(User.active == True)\
153 .filter(User.username == value).one()
153 .filter(User.username == value).one()
154 except Exception:
154 except Exception:
155 raise formencode.Invalid(_('This username is not valid'),
155 raise formencode.Invalid(_('This username is not valid'),
156 value, state)
156 value, state)
157 meta.Session.remove()
157 meta.Session.remove()
158 return self.user_db.user_id
158 return self.user_db.user_id
159
159
160 def ValidRepoName(edit, old_data):
160 def ValidRepoName(edit, old_data):
161 class _ValidRepoName(formencode.validators.FancyValidator):
161 class _ValidRepoName(formencode.validators.FancyValidator):
162
162
163 def to_python(self, value, state):
163 def to_python(self, value, state):
164 slug = h.repo_name_slug(value)
164 slug = h.repo_name_slug(value)
165 if slug in ['_admin']:
165 if slug in ['_admin']:
166 raise formencode.Invalid(_('This repository name is disallowed'),
166 raise formencode.Invalid(_('This repository name is disallowed'),
167 value, state)
167 value, state)
168 if old_data.get('repo_name') != value or not edit:
168 if old_data.get('repo_name') != value or not edit:
169 sa = meta.Session
169 sa = meta.Session
170 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
170 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
171 raise formencode.Invalid(_('This repository already exists') ,
171 raise formencode.Invalid(_('This repository already exists') ,
172 value, state)
172 value, state)
173 meta.Session.remove()
173 meta.Session.remove()
174 return slug
174 return slug
175
175
176
176
177 return _ValidRepoName
177 return _ValidRepoName
178
178
179 class ValidPerms(formencode.validators.FancyValidator):
179 class ValidPerms(formencode.validators.FancyValidator):
180 messages = {'perm_new_user_name':_('This username is not valid')}
180 messages = {'perm_new_user_name':_('This username is not valid')}
181
181
182 def to_python(self, value, state):
182 def to_python(self, value, state):
183 perms_update = []
183 perms_update = []
184 perms_new = []
184 perms_new = []
185 #build a list of permission to update and new permission to create
185 #build a list of permission to update and new permission to create
186 for k, v in value.items():
186 for k, v in value.items():
187 if k.startswith('perm_'):
187 if k.startswith('perm_'):
188 if k.startswith('perm_new_user'):
188 if k.startswith('perm_new_user'):
189 new_perm = value.get('perm_new_user', False)
189 new_perm = value.get('perm_new_user', False)
190 new_user = value.get('perm_new_user_name', False)
190 new_user = value.get('perm_new_user_name', False)
191 if new_user and new_perm:
191 if new_user and new_perm:
192 if (new_user, new_perm) not in perms_new:
192 if (new_user, new_perm) not in perms_new:
193 perms_new.append((new_user, new_perm))
193 perms_new.append((new_user, new_perm))
194 else:
194 else:
195 usr = k[5:]
195 usr = k[5:]
196 if usr == 'default':
196 if usr == 'default':
197 if value['private']:
197 if value['private']:
198 #set none for default when updating to private repo
198 #set none for default when updating to private repo
199 v = 'repository.none'
199 v = 'repository.none'
200 perms_update.append((usr, v))
200 perms_update.append((usr, v))
201 value['perms_updates'] = perms_update
201 value['perms_updates'] = perms_update
202 value['perms_new'] = perms_new
202 value['perms_new'] = perms_new
203 sa = meta.Session
203 sa = meta.Session
204 for k, v in perms_new:
204 for k, v in perms_new:
205 try:
205 try:
206 self.user_db = sa.query(User)\
206 self.user_db = sa.query(User)\
207 .filter(User.active == True)\
207 .filter(User.active == True)\
208 .filter(User.username == k).one()
208 .filter(User.username == k).one()
209 except Exception:
209 except Exception:
210 msg = self.message('perm_new_user_name',
210 msg = self.message('perm_new_user_name',
211 state=State_obj)
211 state=State_obj)
212 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
212 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
213 return value
213 return value
214
214
215 class ValidSettings(formencode.validators.FancyValidator):
215 class ValidSettings(formencode.validators.FancyValidator):
216
216
217 def to_python(self, value, state):
217 def to_python(self, value, state):
218 #settings form can't edit user
218 #settings form can't edit user
219 if value.has_key('user'):
219 if value.has_key('user'):
220 del['value']['user']
220 del['value']['user']
221
221
222 return value
222 return value
223
223
224 class ValidPath(formencode.validators.FancyValidator):
224 class ValidPath(formencode.validators.FancyValidator):
225 def to_python(self, value, state):
225 def to_python(self, value, state):
226 isdir = os.path.isdir(value.replace('*', ''))
226 isdir = os.path.isdir(value.replace('*', ''))
227 if (value.endswith('/*') or value.endswith('/**')) and isdir:
227 if (value.endswith('/*') or value.endswith('/**')) and isdir:
228 return value
228 return value
229 elif not isdir:
229 elif not isdir:
230 msg = _('This is not a valid path')
230 msg = _('This is not a valid path')
231 else:
231 else:
232 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
232 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
233
233
234 raise formencode.Invalid(msg, value, state,
234 raise formencode.Invalid(msg, value, state,
235 error_dict={'paths_root_path':msg})
235 error_dict={'paths_root_path':msg})
236
236
237 #===============================================================================
237 #===============================================================================
238 # FORMS
238 # FORMS
239 #===============================================================================
239 #===============================================================================
240 class LoginForm(formencode.Schema):
240 class LoginForm(formencode.Schema):
241 allow_extra_fields = True
241 allow_extra_fields = True
242 filter_extra_fields = True
242 filter_extra_fields = True
243 username = UnicodeString(
243 username = UnicodeString(
244 strip=True,
244 strip=True,
245 min=3,
245 min=3,
246 not_empty=True,
246 not_empty=True,
247 messages={
247 messages={
248 'empty':_('Please enter a login'),
248 'empty':_('Please enter a login'),
249 'tooShort':_('Enter a value %(min)i characters long or more')}
249 'tooShort':_('Enter a value %(min)i characters long or more')}
250 )
250 )
251
251
252 password = UnicodeString(
252 password = UnicodeString(
253 strip=True,
253 strip=True,
254 min=3,
254 min=3,
255 not_empty=True,
255 not_empty=True,
256 messages={
256 messages={
257 'empty':_('Please enter a password'),
257 'empty':_('Please enter a password'),
258 'tooShort':_('Enter a value %(min)i characters long or more')}
258 'tooShort':_('Enter a value %(min)i characters long or more')}
259 )
259 )
260
260
261
261
262 #chained validators have access to all data
262 #chained validators have access to all data
263 chained_validators = [ValidAuth]
263 chained_validators = [ValidAuth]
264
264
265 def UserForm(edit=False, old_data={}):
265 def UserForm(edit=False, old_data={}):
266 class _UserForm(formencode.Schema):
266 class _UserForm(formencode.Schema):
267 allow_extra_fields = True
267 allow_extra_fields = True
268 filter_extra_fields = True
268 filter_extra_fields = True
269 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
269 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
270 if edit:
270 if edit:
271 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
271 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
272 admin = StringBoolean(if_missing=False)
272 admin = StringBoolean(if_missing=False)
273 else:
273 else:
274 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
274 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
275 active = StringBoolean(if_missing=False)
275 active = StringBoolean(if_missing=False)
276 name = UnicodeString(strip=True, min=3, not_empty=True)
276 name = UnicodeString(strip=True, min=3, not_empty=True)
277 lastname = UnicodeString(strip=True, min=3, not_empty=True)
277 lastname = UnicodeString(strip=True, min=3, not_empty=True)
278 email = Email(not_empty=True)
278 email = Email(not_empty=True)
279
279
280 return _UserForm
280 return _UserForm
281
281
282 RegisterForm = UserForm
282 RegisterForm = UserForm
283
283
284
284
285 def RepoForm(edit=False, old_data={}):
285 def RepoForm(edit=False, old_data={}):
286 class _RepoForm(formencode.Schema):
286 class _RepoForm(formencode.Schema):
287 allow_extra_fields = True
287 allow_extra_fields = True
288 filter_extra_fields = False
288 filter_extra_fields = False
289 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
289 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
290 description = UnicodeString(strip=True, min=3, not_empty=True)
290 description = UnicodeString(strip=True, min=3, not_empty=True)
291 private = StringBoolean(if_missing=False)
291 private = StringBoolean(if_missing=False)
292
292
293 if edit:
293 if edit:
294 user = All(Int(not_empty=True), ValidRepoUser)
294 user = All(Int(not_empty=True), ValidRepoUser)
295
295
296 chained_validators = [ValidPerms]
296 chained_validators = [ValidPerms]
297 return _RepoForm
297 return _RepoForm
298
298
299 def RepoSettingsForm(edit=False, old_data={}):
299 def RepoSettingsForm(edit=False, old_data={}):
300 class _RepoForm(formencode.Schema):
300 class _RepoForm(formencode.Schema):
301 allow_extra_fields = True
301 allow_extra_fields = True
302 filter_extra_fields = False
302 filter_extra_fields = False
303 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
303 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
304 description = UnicodeString(strip=True, min=3, not_empty=True)
304 description = UnicodeString(strip=True, min=3, not_empty=True)
305 private = StringBoolean(if_missing=False)
305 private = StringBoolean(if_missing=False)
306
306
307 chained_validators = [ValidPerms, ValidSettings]
307 chained_validators = [ValidPerms, ValidSettings]
308 return _RepoForm
308 return _RepoForm
309
309
310
310
311 def ApplicationSettingsForm():
311 def ApplicationSettingsForm():
312 class _ApplicationSettingsForm(formencode.Schema):
312 class _ApplicationSettingsForm(formencode.Schema):
313 allow_extra_fields = True
313 allow_extra_fields = True
314 filter_extra_fields = False
314 filter_extra_fields = False
315 hg_app_title = UnicodeString(strip=True, min=3, not_empty=True)
315 hg_app_title = UnicodeString(strip=True, min=3, not_empty=True)
316 hg_app_realm = UnicodeString(strip=True, min=3, not_empty=True)
316 hg_app_realm = UnicodeString(strip=True, min=3, not_empty=True)
317
317
318 return _ApplicationSettingsForm
318 return _ApplicationSettingsForm
319
319
320 def ApplicationUiSettingsForm():
320 def ApplicationUiSettingsForm():
321 class _ApplicationUiSettingsForm(formencode.Schema):
321 class _ApplicationUiSettingsForm(formencode.Schema):
322 allow_extra_fields = True
322 allow_extra_fields = True
323 filter_extra_fields = False
323 filter_extra_fields = False
324 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
324 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
325 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=3, not_empty=True))
325 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=3, not_empty=True))
326 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
326 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
327 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
327 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
328
328
329 return _ApplicationUiSettingsForm
329 return _ApplicationUiSettingsForm
330
330
@@ -1,46 +1,47
1 from pylons_app import get_version
1 from pylons_app import get_version
2 try:
2 try:
3 from setuptools import setup, find_packages
3 from setuptools import setup, find_packages
4 except ImportError:
4 except ImportError:
5 from ez_setup import use_setuptools
5 from ez_setup import use_setuptools
6 use_setuptools()
6 use_setuptools()
7 from setuptools import setup, find_packages
7 from setuptools import setup, find_packages
8
8
9 setup(
9 setup(
10 name='hg_app',
10 name='hg_app',
11 version=get_version(),
11 version=get_version(),
12 description='Mercurial repository serving and browsing app',
12 description='Mercurial repository serving and browsing app',
13 keywords='mercurial web hgwebdir replacement serving hgweb',
13 keywords='mercurial web hgwebdir replacement serving hgweb',
14 license='BSD',
14 license='BSD',
15 author='marcin kuzminski',
15 author='marcin kuzminski',
16 author_email='marcin@python-works.com',
16 author_email='marcin@python-works.com',
17 url='http://hg.python-works.com',
17 url='http://hg.python-works.com',
18 install_requires=[
18 install_requires=[
19 "Pylons>=1.0.0",
19 "Pylons>=1.0.0",
20 "SQLAlchemy>=0.6",
20 "SQLAlchemy>=0.6",
21 "Mako>=0.3.2",
21 "Mako>=0.3.2",
22 "vcs>=0.1.4",
22 "vcs>=0.1.4",
23 "pygments>=1.3.0",
23 "pygments>=1.3.0",
24 "mercurial>=1.6",
24 "mercurial>=1.6",
25 "pysqlite",
25 "pysqlite",
26 "whoosh>=1.0.0b5",
26 "whoosh>=1.0.0b5",
27 "py-bcrypt",
27 ],
28 ],
28 setup_requires=["PasteScript>=1.6.3"],
29 setup_requires=["PasteScript>=1.6.3"],
29 packages=find_packages(exclude=['ez_setup']),
30 packages=find_packages(exclude=['ez_setup']),
30 include_package_data=True,
31 include_package_data=True,
31 test_suite='nose.collector',
32 test_suite='nose.collector',
32 package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']},
33 package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']},
33 message_extractors={'pylons_app': [
34 message_extractors={'pylons_app': [
34 ('**.py', 'python', None),
35 ('**.py', 'python', None),
35 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
36 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
36 ('public/**', 'ignore', None)]},
37 ('public/**', 'ignore', None)]},
37 zip_safe=False,
38 zip_safe=False,
38 paster_plugins=['PasteScript', 'Pylons'],
39 paster_plugins=['PasteScript', 'Pylons'],
39 entry_points="""
40 entry_points="""
40 [paste.app_factory]
41 [paste.app_factory]
41 main = pylons_app.config.middleware:make_app
42 main = pylons_app.config.middleware:make_app
42
43
43 [paste.app_install]
44 [paste.app_install]
44 main = pylons.util:PylonsInstaller
45 main = pylons.util:PylonsInstaller
45 """,
46 """,
46 )
47 )
General Comments 0
You need to be logged in to leave comments. Login now