##// END OF EJS Templates
fixes issue #78, ldap makes user validation caseInsensitive...
marcink -
r741:54684e07 beta
parent child Browse files
Show More
@@ -1,526 +1,534
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 pylons import config, session, url, request
25 from pylons import config, session, url, request
26 from pylons.controllers.util import abort, redirect
26 from pylons.controllers.util import abort, redirect
27 from rhodecode.lib.exceptions import *
27 from rhodecode.lib.exceptions import *
28 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.lib.utils import get_repo_slug
29 from rhodecode.lib.auth_ldap import AuthLdap
29 from rhodecode.lib.auth_ldap import AuthLdap
30 from rhodecode.model import meta
30 from rhodecode.model import meta
31 from rhodecode.model.user import UserModel
31 from rhodecode.model.user import UserModel
32 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.caching_query import FromCache
33 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
33 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
34 UserToPerm
34 UserToPerm
35 import bcrypt
35 import bcrypt
36 from decorator import decorator
36 from decorator import decorator
37 import logging
37 import logging
38 import random
38 import random
39 import traceback
39 import traceback
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 class PasswordGenerator(object):
43 class PasswordGenerator(object):
44 """This is a simple class for generating password from
44 """This is a simple class for generating password from
45 different sets of characters
45 different sets of characters
46 usage:
46 usage:
47 passwd_gen = PasswordGenerator()
47 passwd_gen = PasswordGenerator()
48 #print 8-letter password containing only big and small letters of alphabet
48 #print 8-letter password containing only big and small letters of alphabet
49 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
49 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
50 """
50 """
51 ALPHABETS_NUM = r'''1234567890'''#[0]
51 ALPHABETS_NUM = r'''1234567890'''#[0]
52 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
52 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
53 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
53 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
54 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
54 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
55 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
55 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
56 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
56 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
57 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
57 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
58 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
58 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
59 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
59 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
60
60
61 def __init__(self, passwd=''):
61 def __init__(self, passwd=''):
62 self.passwd = passwd
62 self.passwd = passwd
63
63
64 def gen_password(self, len, type):
64 def gen_password(self, len, type):
65 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
65 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
66 return self.passwd
66 return self.passwd
67
67
68
68
69 def get_crypt_password(password):
69 def get_crypt_password(password):
70 """Cryptographic function used for password hashing based on sha1
70 """Cryptographic function used for password hashing based on sha1
71 :param password: password to hash
71 :param password: password to hash
72 """
72 """
73 return bcrypt.hashpw(password, bcrypt.gensalt(10))
73 return bcrypt.hashpw(password, bcrypt.gensalt(10))
74
74
75 def check_password(password, hashed):
75 def check_password(password, hashed):
76 return bcrypt.hashpw(password, hashed) == hashed
76 return bcrypt.hashpw(password, hashed) == hashed
77
77
78 def authfunc(environ, username, password):
78 def authfunc(environ, username, password):
79 """
79 """
80 Authentication function used in Mercurial/Git/ and access control,
80 Authentication function used in Mercurial/Git/ and access control,
81 firstly checks for db authentication then if ldap is enabled for ldap
81 firstly checks for db authentication then if ldap is enabled for ldap
82 authentication, also creates ldap user if not in database
82 authentication, also creates ldap user if not in database
83
83
84 :param environ: needed only for using in Basic auth, can be None
84 :param environ: needed only for using in Basic auth, can be None
85 :param username: username
85 :param username: username
86 :param password: password
86 :param password: password
87 """
87 """
88 user_model = UserModel()
88 user_model = UserModel()
89 user = user_model.get_by_username(username, cache=False)
89 user = user_model.get_by_username(username, cache=False)
90
90
91 if user is not None and user.is_ldap is False:
91 if user is not None and user.is_ldap is False:
92 if user.active:
92 if user.active:
93
93
94 if user.username == 'default' and user.active:
94 if user.username == 'default' and user.active:
95 log.info('user %s authenticated correctly', username)
95 log.info('user %s authenticated correctly', username)
96 return True
96 return True
97
97
98 elif user.username == username and check_password(password, user.password):
98 elif user.username == username and check_password(password, user.password):
99 log.info('user %s authenticated correctly', username)
99 log.info('user %s authenticated correctly', username)
100 return True
100 return True
101 else:
101 else:
102 log.error('user %s is disabled', username)
102 log.error('user %s is disabled', username)
103
103
104
104
105 else:
105 else:
106
107 #since ldap is searching in case insensitive check if this user is still
108 #not in our system
109 username = username.lower()
110 if user_model.get_by_username(username, cache=False) is not None:
111 return False
112
106 from rhodecode.model.settings import SettingsModel
113 from rhodecode.model.settings import SettingsModel
107 ldap_settings = SettingsModel().get_ldap_settings()
114 ldap_settings = SettingsModel().get_ldap_settings()
108
115
109 #======================================================================
116 #======================================================================
110 # FALLBACK TO LDAP AUTH IN ENABLE
117 # FALLBACK TO LDAP AUTH IN ENABLE
111 #======================================================================
118 #======================================================================
112 if ldap_settings.get('ldap_active', False):
119 if ldap_settings.get('ldap_active', False):
120
113 kwargs = {
121 kwargs = {
114 'server':ldap_settings.get('ldap_host', ''),
122 'server':ldap_settings.get('ldap_host', ''),
115 'base_dn':ldap_settings.get('ldap_base_dn', ''),
123 'base_dn':ldap_settings.get('ldap_base_dn', ''),
116 'port':ldap_settings.get('ldap_port'),
124 'port':ldap_settings.get('ldap_port'),
117 'bind_dn':ldap_settings.get('ldap_dn_user'),
125 'bind_dn':ldap_settings.get('ldap_dn_user'),
118 'bind_pass':ldap_settings.get('ldap_dn_pass'),
126 'bind_pass':ldap_settings.get('ldap_dn_pass'),
119 'use_ldaps':ldap_settings.get('ldap_ldaps'),
127 'use_ldaps':ldap_settings.get('ldap_ldaps'),
120 'ldap_version':3,
128 'ldap_version':3,
121 }
129 }
122 log.debug('Checking for ldap authentication')
130 log.debug('Checking for ldap authentication')
123 try:
131 try:
124 aldap = AuthLdap(**kwargs)
132 aldap = AuthLdap(**kwargs)
125 res = aldap.authenticate_ldap(username, password)
133 res = aldap.authenticate_ldap(username, password)
126
134
127 authenticated = res[1]['uid'][0] == username
135 authenticated = res[1]['uid'][0] == username
128
136
129 if authenticated and user_model.create_ldap(username, password):
137 if authenticated and user_model.create_ldap(username, password):
130 log.info('created new ldap user')
138 log.info('created new ldap user')
131
139
132 return authenticated
140 return authenticated
133 except (LdapUsernameError, LdapPasswordError):
141 except (LdapUsernameError, LdapPasswordError):
134 return False
142 return False
135 except:
143 except:
136 log.error(traceback.format_exc())
144 log.error(traceback.format_exc())
137 return False
145 return False
138 return False
146 return False
139
147
140 class AuthUser(object):
148 class AuthUser(object):
141 """
149 """
142 A simple object that handles a mercurial username for authentication
150 A simple object that handles a mercurial username for authentication
143 """
151 """
144 def __init__(self):
152 def __init__(self):
145 self.username = 'None'
153 self.username = 'None'
146 self.name = ''
154 self.name = ''
147 self.lastname = ''
155 self.lastname = ''
148 self.email = ''
156 self.email = ''
149 self.user_id = None
157 self.user_id = None
150 self.is_authenticated = False
158 self.is_authenticated = False
151 self.is_admin = False
159 self.is_admin = False
152 self.permissions = {}
160 self.permissions = {}
153
161
154 def __repr__(self):
162 def __repr__(self):
155 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
163 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
156
164
157 def set_available_permissions(config):
165 def set_available_permissions(config):
158 """
166 """
159 This function will propagate pylons globals with all available defined
167 This function will propagate pylons globals with all available defined
160 permission given in db. We don't wannt to check each time from db for new
168 permission given in db. We don't wannt to check each time from db for new
161 permissions since adding a new permission also requires application restart
169 permissions since adding a new permission also requires application restart
162 ie. to decorate new views with the newly created permission
170 ie. to decorate new views with the newly created permission
163 :param config:
171 :param config:
164 """
172 """
165 log.info('getting information about all available permissions')
173 log.info('getting information about all available permissions')
166 try:
174 try:
167 sa = meta.Session()
175 sa = meta.Session()
168 all_perms = sa.query(Permission).all()
176 all_perms = sa.query(Permission).all()
169 except:
177 except:
170 pass
178 pass
171 finally:
179 finally:
172 meta.Session.remove()
180 meta.Session.remove()
173
181
174 config['available_permissions'] = [x.permission_name for x in all_perms]
182 config['available_permissions'] = [x.permission_name for x in all_perms]
175
183
176 def set_base_path(config):
184 def set_base_path(config):
177 config['base_path'] = config['pylons.app_globals'].base_path
185 config['base_path'] = config['pylons.app_globals'].base_path
178
186
179
187
180 def fill_perms(user):
188 def fill_perms(user):
181 """
189 """
182 Fills user permission attribute with permissions taken from database
190 Fills user permission attribute with permissions taken from database
183 :param user:
191 :param user:
184 """
192 """
185
193
186 sa = meta.Session()
194 sa = meta.Session()
187 user.permissions['repositories'] = {}
195 user.permissions['repositories'] = {}
188 user.permissions['global'] = set()
196 user.permissions['global'] = set()
189
197
190 #===========================================================================
198 #===========================================================================
191 # fetch default permissions
199 # fetch default permissions
192 #===========================================================================
200 #===========================================================================
193 default_user = UserModel().get_by_username('default', cache=True)
201 default_user = UserModel().get_by_username('default', cache=True)
194
202
195 default_perms = sa.query(RepoToPerm, Repository, Permission)\
203 default_perms = sa.query(RepoToPerm, Repository, Permission)\
196 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
204 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
197 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
205 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
198 .filter(RepoToPerm.user == default_user).all()
206 .filter(RepoToPerm.user == default_user).all()
199
207
200 if user.is_admin:
208 if user.is_admin:
201 #=======================================================================
209 #=======================================================================
202 # #admin have all default rights set to admin
210 # #admin have all default rights set to admin
203 #=======================================================================
211 #=======================================================================
204 user.permissions['global'].add('hg.admin')
212 user.permissions['global'].add('hg.admin')
205
213
206 for perm in default_perms:
214 for perm in default_perms:
207 p = 'repository.admin'
215 p = 'repository.admin'
208 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
216 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
209
217
210 else:
218 else:
211 #=======================================================================
219 #=======================================================================
212 # set default permissions
220 # set default permissions
213 #=======================================================================
221 #=======================================================================
214
222
215 #default global
223 #default global
216 default_global_perms = sa.query(UserToPerm)\
224 default_global_perms = sa.query(UserToPerm)\
217 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
225 .filter(UserToPerm.user == sa.query(User)\
218 'default').one())
226 .filter(User.username == 'default').one())
219
227
220 for perm in default_global_perms:
228 for perm in default_global_perms:
221 user.permissions['global'].add(perm.permission.permission_name)
229 user.permissions['global'].add(perm.permission.permission_name)
222
230
223 #default repositories
231 #default repositories
224 for perm in default_perms:
232 for perm in default_perms:
225 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
233 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
226 #disable defaults for private repos,
234 #disable defaults for private repos,
227 p = 'repository.none'
235 p = 'repository.none'
228 elif perm.Repository.user_id == user.user_id:
236 elif perm.Repository.user_id == user.user_id:
229 #set admin if owner
237 #set admin if owner
230 p = 'repository.admin'
238 p = 'repository.admin'
231 else:
239 else:
232 p = perm.Permission.permission_name
240 p = perm.Permission.permission_name
233
241
234 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
242 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
235
243
236 #=======================================================================
244 #=======================================================================
237 # #overwrite default with user permissions if any
245 # #overwrite default with user permissions if any
238 #=======================================================================
246 #=======================================================================
239 user_perms = sa.query(RepoToPerm, Permission, Repository)\
247 user_perms = sa.query(RepoToPerm, Permission, Repository)\
240 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
248 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
241 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
249 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
242 .filter(RepoToPerm.user_id == user.user_id).all()
250 .filter(RepoToPerm.user_id == user.user_id).all()
243
251
244 for perm in user_perms:
252 for perm in user_perms:
245 if perm.Repository.user_id == user.user_id:#set admin if owner
253 if perm.Repository.user_id == user.user_id:#set admin if owner
246 p = 'repository.admin'
254 p = 'repository.admin'
247 else:
255 else:
248 p = perm.Permission.permission_name
256 p = perm.Permission.permission_name
249 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
257 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
250 meta.Session.remove()
258 meta.Session.remove()
251 return user
259 return user
252
260
253 def get_user(session):
261 def get_user(session):
254 """
262 """
255 Gets user from session, and wraps permissions into user
263 Gets user from session, and wraps permissions into user
256 :param session:
264 :param session:
257 """
265 """
258 user = session.get('rhodecode_user', AuthUser())
266 user = session.get('rhodecode_user', AuthUser())
259 #if the user is not logged in we check for anonymous access
267 #if the user is not logged in we check for anonymous access
260 #if user is logged and it's a default user check if we still have anonymous
268 #if user is logged and it's a default user check if we still have anonymous
261 #access enabled
269 #access enabled
262 if user.user_id is None or user.username == 'default':
270 if user.user_id is None or user.username == 'default':
263 anonymous_user = UserModel().get_by_username('default', cache=True)
271 anonymous_user = UserModel().get_by_username('default', cache=True)
264 if anonymous_user.active is True:
272 if anonymous_user.active is True:
265 #then we set this user is logged in
273 #then we set this user is logged in
266 user.is_authenticated = True
274 user.is_authenticated = True
267 user.user_id = anonymous_user.user_id
275 user.user_id = anonymous_user.user_id
268 else:
276 else:
269 user.is_authenticated = False
277 user.is_authenticated = False
270
278
271 if user.is_authenticated:
279 if user.is_authenticated:
272 user = UserModel().fill_data(user)
280 user = UserModel().fill_data(user)
273
281
274 user = fill_perms(user)
282 user = fill_perms(user)
275 session['rhodecode_user'] = user
283 session['rhodecode_user'] = user
276 session.save()
284 session.save()
277 return user
285 return user
278
286
279 #===============================================================================
287 #===============================================================================
280 # CHECK DECORATORS
288 # CHECK DECORATORS
281 #===============================================================================
289 #===============================================================================
282 class LoginRequired(object):
290 class LoginRequired(object):
283 """Must be logged in to execute this function else redirect to login page"""
291 """Must be logged in to execute this function else redirect to login page"""
284
292
285 def __call__(self, func):
293 def __call__(self, func):
286 return decorator(self.__wrapper, func)
294 return decorator(self.__wrapper, func)
287
295
288 def __wrapper(self, func, *fargs, **fkwargs):
296 def __wrapper(self, func, *fargs, **fkwargs):
289 user = session.get('rhodecode_user', AuthUser())
297 user = session.get('rhodecode_user', AuthUser())
290 log.debug('Checking login required for user:%s', user.username)
298 log.debug('Checking login required for user:%s', user.username)
291 if user.is_authenticated:
299 if user.is_authenticated:
292 log.debug('user %s is authenticated', user.username)
300 log.debug('user %s is authenticated', user.username)
293 return func(*fargs, **fkwargs)
301 return func(*fargs, **fkwargs)
294 else:
302 else:
295 log.warn('user %s not authenticated', user.username)
303 log.warn('user %s not authenticated', user.username)
296
304
297 p = ''
305 p = ''
298 if request.environ.get('SCRIPT_NAME') != '/':
306 if request.environ.get('SCRIPT_NAME') != '/':
299 p += request.environ.get('SCRIPT_NAME')
307 p += request.environ.get('SCRIPT_NAME')
300
308
301 p += request.environ.get('PATH_INFO')
309 p += request.environ.get('PATH_INFO')
302 if request.environ.get('QUERY_STRING'):
310 if request.environ.get('QUERY_STRING'):
303 p += '?' + request.environ.get('QUERY_STRING')
311 p += '?' + request.environ.get('QUERY_STRING')
304
312
305 log.debug('redirecting to login page with %s', p)
313 log.debug('redirecting to login page with %s', p)
306 return redirect(url('login_home', came_from=p))
314 return redirect(url('login_home', came_from=p))
307
315
308 class PermsDecorator(object):
316 class PermsDecorator(object):
309 """Base class for decorators"""
317 """Base class for decorators"""
310
318
311 def __init__(self, *required_perms):
319 def __init__(self, *required_perms):
312 available_perms = config['available_permissions']
320 available_perms = config['available_permissions']
313 for perm in required_perms:
321 for perm in required_perms:
314 if perm not in available_perms:
322 if perm not in available_perms:
315 raise Exception("'%s' permission is not defined" % perm)
323 raise Exception("'%s' permission is not defined" % perm)
316 self.required_perms = set(required_perms)
324 self.required_perms = set(required_perms)
317 self.user_perms = None
325 self.user_perms = None
318
326
319 def __call__(self, func):
327 def __call__(self, func):
320 return decorator(self.__wrapper, func)
328 return decorator(self.__wrapper, func)
321
329
322
330
323 def __wrapper(self, func, *fargs, **fkwargs):
331 def __wrapper(self, func, *fargs, **fkwargs):
324 # _wrapper.__name__ = func.__name__
332 # _wrapper.__name__ = func.__name__
325 # _wrapper.__dict__.update(func.__dict__)
333 # _wrapper.__dict__.update(func.__dict__)
326 # _wrapper.__doc__ = func.__doc__
334 # _wrapper.__doc__ = func.__doc__
327 self.user = session.get('rhodecode_user', AuthUser())
335 self.user = session.get('rhodecode_user', AuthUser())
328 self.user_perms = self.user.permissions
336 self.user_perms = self.user.permissions
329 log.debug('checking %s permissions %s for %s %s',
337 log.debug('checking %s permissions %s for %s %s',
330 self.__class__.__name__, self.required_perms, func.__name__,
338 self.__class__.__name__, self.required_perms, func.__name__,
331 self.user)
339 self.user)
332
340
333 if self.check_permissions():
341 if self.check_permissions():
334 log.debug('Permission granted for %s %s', func.__name__, self.user)
342 log.debug('Permission granted for %s %s', func.__name__, self.user)
335
343
336 return func(*fargs, **fkwargs)
344 return func(*fargs, **fkwargs)
337
345
338 else:
346 else:
339 log.warning('Permission denied for %s %s', func.__name__, self.user)
347 log.warning('Permission denied for %s %s', func.__name__, self.user)
340 #redirect with forbidden ret code
348 #redirect with forbidden ret code
341 return abort(403)
349 return abort(403)
342
350
343
351
344
352
345 def check_permissions(self):
353 def check_permissions(self):
346 """Dummy function for overriding"""
354 """Dummy function for overriding"""
347 raise Exception('You have to write this function in child class')
355 raise Exception('You have to write this function in child class')
348
356
349 class HasPermissionAllDecorator(PermsDecorator):
357 class HasPermissionAllDecorator(PermsDecorator):
350 """Checks for access permission for all given predicates. All of them
358 """Checks for access permission for all given predicates. All of them
351 have to be meet in order to fulfill the request
359 have to be meet in order to fulfill the request
352 """
360 """
353
361
354 def check_permissions(self):
362 def check_permissions(self):
355 if self.required_perms.issubset(self.user_perms.get('global')):
363 if self.required_perms.issubset(self.user_perms.get('global')):
356 return True
364 return True
357 return False
365 return False
358
366
359
367
360 class HasPermissionAnyDecorator(PermsDecorator):
368 class HasPermissionAnyDecorator(PermsDecorator):
361 """Checks for access permission for any of given predicates. In order to
369 """Checks for access permission for any of given predicates. In order to
362 fulfill the request any of predicates must be meet
370 fulfill the request any of predicates must be meet
363 """
371 """
364
372
365 def check_permissions(self):
373 def check_permissions(self):
366 if self.required_perms.intersection(self.user_perms.get('global')):
374 if self.required_perms.intersection(self.user_perms.get('global')):
367 return True
375 return True
368 return False
376 return False
369
377
370 class HasRepoPermissionAllDecorator(PermsDecorator):
378 class HasRepoPermissionAllDecorator(PermsDecorator):
371 """Checks for access permission for all given predicates for specific
379 """Checks for access permission for all given predicates for specific
372 repository. All of them have to be meet in order to fulfill the request
380 repository. All of them have to be meet in order to fulfill the request
373 """
381 """
374
382
375 def check_permissions(self):
383 def check_permissions(self):
376 repo_name = get_repo_slug(request)
384 repo_name = get_repo_slug(request)
377 try:
385 try:
378 user_perms = set([self.user_perms['repositories'][repo_name]])
386 user_perms = set([self.user_perms['repositories'][repo_name]])
379 except KeyError:
387 except KeyError:
380 return False
388 return False
381 if self.required_perms.issubset(user_perms):
389 if self.required_perms.issubset(user_perms):
382 return True
390 return True
383 return False
391 return False
384
392
385
393
386 class HasRepoPermissionAnyDecorator(PermsDecorator):
394 class HasRepoPermissionAnyDecorator(PermsDecorator):
387 """Checks for access permission for any of given predicates for specific
395 """Checks for access permission for any of given predicates for specific
388 repository. In order to fulfill the request any of predicates must be meet
396 repository. In order to fulfill the request any of predicates must be meet
389 """
397 """
390
398
391 def check_permissions(self):
399 def check_permissions(self):
392 repo_name = get_repo_slug(request)
400 repo_name = get_repo_slug(request)
393
401
394 try:
402 try:
395 user_perms = set([self.user_perms['repositories'][repo_name]])
403 user_perms = set([self.user_perms['repositories'][repo_name]])
396 except KeyError:
404 except KeyError:
397 return False
405 return False
398 if self.required_perms.intersection(user_perms):
406 if self.required_perms.intersection(user_perms):
399 return True
407 return True
400 return False
408 return False
401 #===============================================================================
409 #===============================================================================
402 # CHECK FUNCTIONS
410 # CHECK FUNCTIONS
403 #===============================================================================
411 #===============================================================================
404
412
405 class PermsFunction(object):
413 class PermsFunction(object):
406 """Base function for other check functions"""
414 """Base function for other check functions"""
407
415
408 def __init__(self, *perms):
416 def __init__(self, *perms):
409 available_perms = config['available_permissions']
417 available_perms = config['available_permissions']
410
418
411 for perm in perms:
419 for perm in perms:
412 if perm not in available_perms:
420 if perm not in available_perms:
413 raise Exception("'%s' permission in not defined" % perm)
421 raise Exception("'%s' permission in not defined" % perm)
414 self.required_perms = set(perms)
422 self.required_perms = set(perms)
415 self.user_perms = None
423 self.user_perms = None
416 self.granted_for = ''
424 self.granted_for = ''
417 self.repo_name = None
425 self.repo_name = None
418
426
419 def __call__(self, check_Location=''):
427 def __call__(self, check_Location=''):
420 user = session.get('rhodecode_user', False)
428 user = session.get('rhodecode_user', False)
421 if not user:
429 if not user:
422 return False
430 return False
423 self.user_perms = user.permissions
431 self.user_perms = user.permissions
424 self.granted_for = user.username
432 self.granted_for = user.username
425 log.debug('checking %s %s %s', self.__class__.__name__,
433 log.debug('checking %s %s %s', self.__class__.__name__,
426 self.required_perms, user)
434 self.required_perms, user)
427
435
428 if self.check_permissions():
436 if self.check_permissions():
429 log.debug('Permission granted for %s @ %s %s', self.granted_for,
437 log.debug('Permission granted for %s @ %s %s', self.granted_for,
430 check_Location, user)
438 check_Location, user)
431 return True
439 return True
432
440
433 else:
441 else:
434 log.warning('Permission denied for %s @ %s %s', self.granted_for,
442 log.warning('Permission denied for %s @ %s %s', self.granted_for,
435 check_Location, user)
443 check_Location, user)
436 return False
444 return False
437
445
438 def check_permissions(self):
446 def check_permissions(self):
439 """Dummy function for overriding"""
447 """Dummy function for overriding"""
440 raise Exception('You have to write this function in child class')
448 raise Exception('You have to write this function in child class')
441
449
442 class HasPermissionAll(PermsFunction):
450 class HasPermissionAll(PermsFunction):
443 def check_permissions(self):
451 def check_permissions(self):
444 if self.required_perms.issubset(self.user_perms.get('global')):
452 if self.required_perms.issubset(self.user_perms.get('global')):
445 return True
453 return True
446 return False
454 return False
447
455
448 class HasPermissionAny(PermsFunction):
456 class HasPermissionAny(PermsFunction):
449 def check_permissions(self):
457 def check_permissions(self):
450 if self.required_perms.intersection(self.user_perms.get('global')):
458 if self.required_perms.intersection(self.user_perms.get('global')):
451 return True
459 return True
452 return False
460 return False
453
461
454 class HasRepoPermissionAll(PermsFunction):
462 class HasRepoPermissionAll(PermsFunction):
455
463
456 def __call__(self, repo_name=None, check_Location=''):
464 def __call__(self, repo_name=None, check_Location=''):
457 self.repo_name = repo_name
465 self.repo_name = repo_name
458 return super(HasRepoPermissionAll, self).__call__(check_Location)
466 return super(HasRepoPermissionAll, self).__call__(check_Location)
459
467
460 def check_permissions(self):
468 def check_permissions(self):
461 if not self.repo_name:
469 if not self.repo_name:
462 self.repo_name = get_repo_slug(request)
470 self.repo_name = get_repo_slug(request)
463
471
464 try:
472 try:
465 self.user_perms = set([self.user_perms['repositories']\
473 self.user_perms = set([self.user_perms['repositories']\
466 [self.repo_name]])
474 [self.repo_name]])
467 except KeyError:
475 except KeyError:
468 return False
476 return False
469 self.granted_for = self.repo_name
477 self.granted_for = self.repo_name
470 if self.required_perms.issubset(self.user_perms):
478 if self.required_perms.issubset(self.user_perms):
471 return True
479 return True
472 return False
480 return False
473
481
474 class HasRepoPermissionAny(PermsFunction):
482 class HasRepoPermissionAny(PermsFunction):
475
483
476 def __call__(self, repo_name=None, check_Location=''):
484 def __call__(self, repo_name=None, check_Location=''):
477 self.repo_name = repo_name
485 self.repo_name = repo_name
478 return super(HasRepoPermissionAny, self).__call__(check_Location)
486 return super(HasRepoPermissionAny, self).__call__(check_Location)
479
487
480 def check_permissions(self):
488 def check_permissions(self):
481 if not self.repo_name:
489 if not self.repo_name:
482 self.repo_name = get_repo_slug(request)
490 self.repo_name = get_repo_slug(request)
483
491
484 try:
492 try:
485 self.user_perms = set([self.user_perms['repositories']\
493 self.user_perms = set([self.user_perms['repositories']\
486 [self.repo_name]])
494 [self.repo_name]])
487 except KeyError:
495 except KeyError:
488 return False
496 return False
489 self.granted_for = self.repo_name
497 self.granted_for = self.repo_name
490 if self.required_perms.intersection(self.user_perms):
498 if self.required_perms.intersection(self.user_perms):
491 return True
499 return True
492 return False
500 return False
493
501
494 #===============================================================================
502 #===============================================================================
495 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
503 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
496 #===============================================================================
504 #===============================================================================
497
505
498 class HasPermissionAnyMiddleware(object):
506 class HasPermissionAnyMiddleware(object):
499 def __init__(self, *perms):
507 def __init__(self, *perms):
500 self.required_perms = set(perms)
508 self.required_perms = set(perms)
501
509
502 def __call__(self, user, repo_name):
510 def __call__(self, user, repo_name):
503 usr = AuthUser()
511 usr = AuthUser()
504 usr.user_id = user.user_id
512 usr.user_id = user.user_id
505 usr.username = user.username
513 usr.username = user.username
506 usr.is_admin = user.admin
514 usr.is_admin = user.admin
507
515
508 try:
516 try:
509 self.user_perms = set([fill_perms(usr)\
517 self.user_perms = set([fill_perms(usr)\
510 .permissions['repositories'][repo_name]])
518 .permissions['repositories'][repo_name]])
511 except:
519 except:
512 self.user_perms = set()
520 self.user_perms = set()
513 self.granted_for = ''
521 self.granted_for = ''
514 self.username = user.username
522 self.username = user.username
515 self.repo_name = repo_name
523 self.repo_name = repo_name
516 return self.check_permissions()
524 return self.check_permissions()
517
525
518 def check_permissions(self):
526 def check_permissions(self):
519 log.debug('checking mercurial protocol '
527 log.debug('checking mercurial protocol '
520 'permissions for user:%s repository:%s',
528 'permissions for user:%s repository:%s',
521 self.username, self.repo_name)
529 self.username, self.repo_name)
522 if self.required_perms.intersection(self.user_perms):
530 if self.required_perms.intersection(self.user_perms):
523 log.debug('permission granted')
531 log.debug('permission granted')
524 return True
532 return True
525 log.debug('permission denied')
533 log.debug('permission denied')
526 return False
534 return False
@@ -1,438 +1,441
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 rhodecode.lib.auth import authfunc, get_crypt_password
27 from rhodecode.lib.auth import authfunc, get_crypt_password
28 from rhodecode.lib.exceptions import LdapImportError
28 from rhodecode.lib.exceptions import LdapImportError
29 from rhodecode.model import meta
29 from rhodecode.model import meta
30 from rhodecode.model.user import UserModel
30 from rhodecode.model.user import UserModel
31 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.db import User
32 from rhodecode.model.db import User
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34 from rhodecode import BACKENDS
34 from rhodecode import BACKENDS
35 import formencode
35 import formencode
36 import logging
36 import logging
37 import os
37 import os
38 import rhodecode.lib.helpers as h
38 import rhodecode.lib.helpers as h
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42 #this is needed to translate the messages using _() in validators
42 #this is needed to translate the messages using _() in validators
43 class State_obj(object):
43 class State_obj(object):
44 _ = staticmethod(_)
44 _ = staticmethod(_)
45
45
46 #===============================================================================
46 #===============================================================================
47 # VALIDATORS
47 # VALIDATORS
48 #===============================================================================
48 #===============================================================================
49 class ValidAuthToken(formencode.validators.FancyValidator):
49 class ValidAuthToken(formencode.validators.FancyValidator):
50 messages = {'invalid_token':_('Token mismatch')}
50 messages = {'invalid_token':_('Token mismatch')}
51
51
52 def validate_python(self, value, state):
52 def validate_python(self, value, state):
53
53
54 if value != authentication_token():
54 if value != authentication_token():
55 raise formencode.Invalid(self.message('invalid_token', state,
55 raise formencode.Invalid(self.message('invalid_token', state,
56 search_number=value), value, state)
56 search_number=value), value, state)
57
57
58 def ValidUsername(edit, old_data):
58 def ValidUsername(edit, old_data):
59 class _ValidUsername(formencode.validators.FancyValidator):
59 class _ValidUsername(formencode.validators.FancyValidator):
60
60
61 def validate_python(self, value, state):
61 def validate_python(self, value, state):
62 if value in ['default', 'new_user']:
62 if value in ['default', 'new_user']:
63 raise formencode.Invalid(_('Invalid username'), value, state)
63 raise formencode.Invalid(_('Invalid username'), value, state)
64 #check if user is unique
64 #check if user is unique
65 old_un = None
65 old_un = None
66 if edit:
66 if edit:
67 old_un = UserModel().get(old_data.get('user_id')).username
67 old_un = UserModel().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 UserModel().get_by_username(value, cache=False):
70 if UserModel().get_by_username(value.lower(), cache=False):
71 raise formencode.Invalid(_('This username already exists') ,
71 raise formencode.Invalid(_('This username already exists') ,
72 value, state)
72 value, state)
73
73
74 return _ValidUsername
74 return _ValidUsername
75
75
76 class ValidPassword(formencode.validators.FancyValidator):
76 class ValidPassword(formencode.validators.FancyValidator):
77
77
78 def to_python(self, value, state):
78 def to_python(self, value, state):
79
79
80 if value:
80 if value:
81
81
82 if value.get('password'):
82 if value.get('password'):
83 try:
83 try:
84 value['password'] = get_crypt_password(value['password'])
84 value['password'] = get_crypt_password(value['password'])
85 except UnicodeEncodeError:
85 except UnicodeEncodeError:
86 e_dict = {'password':_('Invalid characters in password')}
86 e_dict = {'password':_('Invalid characters in password')}
87 raise formencode.Invalid('', value, state, error_dict=e_dict)
87 raise formencode.Invalid('', value, state, error_dict=e_dict)
88
88
89 if value.get('password_confirmation'):
89 if value.get('password_confirmation'):
90 try:
90 try:
91 value['password_confirmation'] = \
91 value['password_confirmation'] = \
92 get_crypt_password(value['password_confirmation'])
92 get_crypt_password(value['password_confirmation'])
93 except UnicodeEncodeError:
93 except UnicodeEncodeError:
94 e_dict = {'password_confirmation':_('Invalid characters in password')}
94 e_dict = {'password_confirmation':_('Invalid characters in password')}
95 raise formencode.Invalid('', value, state, error_dict=e_dict)
95 raise formencode.Invalid('', value, state, error_dict=e_dict)
96
96
97 if value.get('new_password'):
97 if value.get('new_password'):
98 try:
98 try:
99 value['new_password'] = \
99 value['new_password'] = \
100 get_crypt_password(value['new_password'])
100 get_crypt_password(value['new_password'])
101 except UnicodeEncodeError:
101 except UnicodeEncodeError:
102 e_dict = {'new_password':_('Invalid characters in password')}
102 e_dict = {'new_password':_('Invalid characters in password')}
103 raise formencode.Invalid('', value, state, error_dict=e_dict)
103 raise formencode.Invalid('', value, state, error_dict=e_dict)
104
104
105 return value
105 return value
106
106
107 class ValidPasswordsMatch(formencode.validators.FancyValidator):
107 class ValidPasswordsMatch(formencode.validators.FancyValidator):
108
108
109 def validate_python(self, value, state):
109 def validate_python(self, value, state):
110
110
111 if value['password'] != value['password_confirmation']:
111 if value['password'] != value['password_confirmation']:
112 e_dict = {'password_confirmation':
112 e_dict = {'password_confirmation':
113 _('Password do not match')}
113 _('Password do not match')}
114 raise formencode.Invalid('', value, state, error_dict=e_dict)
114 raise formencode.Invalid('', value, state, error_dict=e_dict)
115
115
116 class ValidAuth(formencode.validators.FancyValidator):
116 class ValidAuth(formencode.validators.FancyValidator):
117 messages = {
117 messages = {
118 'invalid_password':_('invalid password'),
118 'invalid_password':_('invalid password'),
119 'invalid_login':_('invalid user name'),
119 'invalid_login':_('invalid user name'),
120 'disabled_account':_('Your account is disabled')
120 'disabled_account':_('Your account is disabled')
121
121
122 }
122 }
123 #error mapping
123 #error mapping
124 e_dict = {'username':messages['invalid_login'],
124 e_dict = {'username':messages['invalid_login'],
125 'password':messages['invalid_password']}
125 'password':messages['invalid_password']}
126 e_dict_disable = {'username':messages['disabled_account']}
126 e_dict_disable = {'username':messages['disabled_account']}
127
127
128 def validate_python(self, value, state):
128 def validate_python(self, value, state):
129 password = value['password']
129 password = value['password']
130 username = value['username']
130 username = value['username']
131 user = UserModel().get_by_username(username)
131 user = UserModel().get_by_username(username)
132
132
133 if authfunc(None, username, password):
133 if authfunc(None, username, password):
134 return value
134 return value
135 else:
135 else:
136 if user and user.active is False:
136 if user and user.active is False:
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 else:
142 else:
143 log.warning('user %s not authenticated', username)
143 log.warning('user %s not authenticated', username)
144 raise formencode.Invalid(self.message('invalid_password',
144 raise formencode.Invalid(self.message('invalid_password',
145 state=State_obj), value, state,
145 state=State_obj), value, state,
146 error_dict=self.e_dict)
146 error_dict=self.e_dict)
147
147
148 class ValidRepoUser(formencode.validators.FancyValidator):
148 class ValidRepoUser(formencode.validators.FancyValidator):
149
149
150 def to_python(self, value, state):
150 def to_python(self, value, state):
151 sa = meta.Session()
151 sa = meta.Session()
152 try:
152 try:
153 self.user_db = sa.query(User)\
153 self.user_db = sa.query(User)\
154 .filter(User.active == True)\
154 .filter(User.active == True)\
155 .filter(User.username == value).one()
155 .filter(User.username == value).one()
156 except Exception:
156 except Exception:
157 raise formencode.Invalid(_('This username is not valid'),
157 raise formencode.Invalid(_('This username is not valid'),
158 value, state)
158 value, state)
159 finally:
159 finally:
160 meta.Session.remove()
160 meta.Session.remove()
161
161
162 return self.user_db.user_id
162 return self.user_db.user_id
163
163
164 def ValidRepoName(edit, old_data):
164 def ValidRepoName(edit, old_data):
165 class _ValidRepoName(formencode.validators.FancyValidator):
165 class _ValidRepoName(formencode.validators.FancyValidator):
166
166
167 def to_python(self, value, state):
167 def to_python(self, value, state):
168 slug = h.repo_name_slug(value)
168 slug = h.repo_name_slug(value)
169 if slug in ['_admin']:
169 if slug in ['_admin']:
170 raise formencode.Invalid(_('This repository name is disallowed'),
170 raise formencode.Invalid(_('This repository name is disallowed'),
171 value, state)
171 value, state)
172 if old_data.get('repo_name') != value or not edit:
172 if old_data.get('repo_name') != value or not edit:
173 if RepoModel().get_by_repo_name(slug, cache=False):
173 if RepoModel().get_by_repo_name(slug, cache=False):
174 raise formencode.Invalid(_('This repository already exists') ,
174 raise formencode.Invalid(_('This repository already exists') ,
175 value, state)
175 value, state)
176 return slug
176 return slug
177
177
178
178
179 return _ValidRepoName
179 return _ValidRepoName
180
180
181 def ValidForkType(old_data):
181 def ValidForkType(old_data):
182 class _ValidForkType(formencode.validators.FancyValidator):
182 class _ValidForkType(formencode.validators.FancyValidator):
183
183
184 def to_python(self, value, state):
184 def to_python(self, value, state):
185 if old_data['repo_type'] != value:
185 if old_data['repo_type'] != value:
186 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
186 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
187 return value
187 return value
188 return _ValidForkType
188 return _ValidForkType
189
189
190 class ValidPerms(formencode.validators.FancyValidator):
190 class ValidPerms(formencode.validators.FancyValidator):
191 messages = {'perm_new_user_name':_('This username is not valid')}
191 messages = {'perm_new_user_name':_('This username is not valid')}
192
192
193 def to_python(self, value, state):
193 def to_python(self, value, state):
194 perms_update = []
194 perms_update = []
195 perms_new = []
195 perms_new = []
196 #build a list of permission to update and new permission to create
196 #build a list of permission to update and new permission to create
197 for k, v in value.items():
197 for k, v in value.items():
198 if k.startswith('perm_'):
198 if k.startswith('perm_'):
199 if k.startswith('perm_new_user'):
199 if k.startswith('perm_new_user'):
200 new_perm = value.get('perm_new_user', False)
200 new_perm = value.get('perm_new_user', False)
201 new_user = value.get('perm_new_user_name', False)
201 new_user = value.get('perm_new_user_name', False)
202 if new_user and new_perm:
202 if new_user and new_perm:
203 if (new_user, new_perm) not in perms_new:
203 if (new_user, new_perm) not in perms_new:
204 perms_new.append((new_user, new_perm))
204 perms_new.append((new_user, new_perm))
205 else:
205 else:
206 usr = k[5:]
206 usr = k[5:]
207 if usr == 'default':
207 if usr == 'default':
208 if value['private']:
208 if value['private']:
209 #set none for default when updating to private repo
209 #set none for default when updating to private repo
210 v = 'repository.none'
210 v = 'repository.none'
211 perms_update.append((usr, v))
211 perms_update.append((usr, v))
212 value['perms_updates'] = perms_update
212 value['perms_updates'] = perms_update
213 value['perms_new'] = perms_new
213 value['perms_new'] = perms_new
214 sa = meta.Session
214 sa = meta.Session
215 for k, v in perms_new:
215 for k, v in perms_new:
216 try:
216 try:
217 self.user_db = sa.query(User)\
217 self.user_db = sa.query(User)\
218 .filter(User.active == True)\
218 .filter(User.active == True)\
219 .filter(User.username == k).one()
219 .filter(User.username == k).one()
220 except Exception:
220 except Exception:
221 msg = self.message('perm_new_user_name',
221 msg = self.message('perm_new_user_name',
222 state=State_obj)
222 state=State_obj)
223 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
223 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
224 return value
224 return value
225
225
226 class ValidSettings(formencode.validators.FancyValidator):
226 class ValidSettings(formencode.validators.FancyValidator):
227
227
228 def to_python(self, value, state):
228 def to_python(self, value, state):
229 #settings form can't edit user
229 #settings form can't edit user
230 if value.has_key('user'):
230 if value.has_key('user'):
231 del['value']['user']
231 del['value']['user']
232
232
233 return value
233 return value
234
234
235 class ValidPath(formencode.validators.FancyValidator):
235 class ValidPath(formencode.validators.FancyValidator):
236 def to_python(self, value, state):
236 def to_python(self, value, state):
237
237
238 if not os.path.isdir(value):
238 if not os.path.isdir(value):
239 msg = _('This is not a valid path')
239 msg = _('This is not a valid path')
240 raise formencode.Invalid(msg, value, state,
240 raise formencode.Invalid(msg, value, state,
241 error_dict={'paths_root_path':msg})
241 error_dict={'paths_root_path':msg})
242 return value
242 return value
243
243
244 def UniqSystemEmail(old_data):
244 def UniqSystemEmail(old_data):
245 class _UniqSystemEmail(formencode.validators.FancyValidator):
245 class _UniqSystemEmail(formencode.validators.FancyValidator):
246 def to_python(self, value, state):
246 def to_python(self, value, state):
247 value = value.lower()
248 #TODO:write test for MixedCase scenarios
247 if old_data.get('email') != value:
249 if old_data.get('email') != value:
248 sa = meta.Session()
250 sa = meta.Session()
249 try:
251 try:
250 user = sa.query(User).filter(User.email == value).scalar()
252 user = sa.query(User).filter(User.email == value).scalar()
251 if user:
253 if user:
252 raise formencode.Invalid(_("That e-mail address is already taken") ,
254 raise formencode.Invalid(_("That e-mail address is already taken") ,
253 value, state)
255 value, state)
254 finally:
256 finally:
255 meta.Session.remove()
257 meta.Session.remove()
256
258
257 return value
259 return value
258
260
259 return _UniqSystemEmail
261 return _UniqSystemEmail
260
262
261 class ValidSystemEmail(formencode.validators.FancyValidator):
263 class ValidSystemEmail(formencode.validators.FancyValidator):
262 def to_python(self, value, state):
264 def to_python(self, value, state):
265 value = value.lower()
263 sa = meta.Session
266 sa = meta.Session
264 try:
267 try:
265 user = sa.query(User).filter(User.email == value).scalar()
268 user = sa.query(User).filter(User.email == value).scalar()
266 if user is None:
269 if user is None:
267 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
270 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
268 value, state)
271 value, state)
269 finally:
272 finally:
270 meta.Session.remove()
273 meta.Session.remove()
271
274
272 return value
275 return value
273
276
274 class LdapLibValidator(formencode.validators.FancyValidator):
277 class LdapLibValidator(formencode.validators.FancyValidator):
275
278
276 def to_python(self, value, state):
279 def to_python(self, value, state):
277
280
278 try:
281 try:
279 import ldap
282 import ldap
280 except ImportError:
283 except ImportError:
281 raise LdapImportError
284 raise LdapImportError
282 return value
285 return value
283
286
284 #===============================================================================
287 #===============================================================================
285 # FORMS
288 # FORMS
286 #===============================================================================
289 #===============================================================================
287 class LoginForm(formencode.Schema):
290 class LoginForm(formencode.Schema):
288 allow_extra_fields = True
291 allow_extra_fields = True
289 filter_extra_fields = True
292 filter_extra_fields = True
290 username = UnicodeString(
293 username = UnicodeString(
291 strip=True,
294 strip=True,
292 min=1,
295 min=1,
293 not_empty=True,
296 not_empty=True,
294 messages={
297 messages={
295 'empty':_('Please enter a login'),
298 'empty':_('Please enter a login'),
296 'tooShort':_('Enter a value %(min)i characters long or more')}
299 'tooShort':_('Enter a value %(min)i characters long or more')}
297 )
300 )
298
301
299 password = UnicodeString(
302 password = UnicodeString(
300 strip=True,
303 strip=True,
301 min=6,
304 min=6,
302 not_empty=True,
305 not_empty=True,
303 messages={
306 messages={
304 'empty':_('Please enter a password'),
307 'empty':_('Please enter a password'),
305 'tooShort':_('Enter %(min)i characters or more')}
308 'tooShort':_('Enter %(min)i characters or more')}
306 )
309 )
307
310
308
311
309 #chained validators have access to all data
312 #chained validators have access to all data
310 chained_validators = [ValidAuth]
313 chained_validators = [ValidAuth]
311
314
312 def UserForm(edit=False, old_data={}):
315 def UserForm(edit=False, old_data={}):
313 class _UserForm(formencode.Schema):
316 class _UserForm(formencode.Schema):
314 allow_extra_fields = True
317 allow_extra_fields = True
315 filter_extra_fields = True
318 filter_extra_fields = True
316 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
319 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
317 if edit:
320 if edit:
318 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
321 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
319 admin = StringBoolean(if_missing=False)
322 admin = StringBoolean(if_missing=False)
320 else:
323 else:
321 password = All(UnicodeString(strip=True, min=6, not_empty=True))
324 password = All(UnicodeString(strip=True, min=6, not_empty=True))
322 active = StringBoolean(if_missing=False)
325 active = StringBoolean(if_missing=False)
323 name = UnicodeString(strip=True, min=1, not_empty=True)
326 name = UnicodeString(strip=True, min=1, not_empty=True)
324 lastname = UnicodeString(strip=True, min=1, not_empty=True)
327 lastname = UnicodeString(strip=True, min=1, not_empty=True)
325 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
328 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
326
329
327 chained_validators = [ValidPassword]
330 chained_validators = [ValidPassword]
328
331
329 return _UserForm
332 return _UserForm
330
333
331 def RegisterForm(edit=False, old_data={}):
334 def RegisterForm(edit=False, old_data={}):
332 class _RegisterForm(formencode.Schema):
335 class _RegisterForm(formencode.Schema):
333 allow_extra_fields = True
336 allow_extra_fields = True
334 filter_extra_fields = True
337 filter_extra_fields = True
335 username = All(ValidUsername(edit, old_data), UnicodeString(strip=True, min=1, not_empty=True))
338 username = All(ValidUsername(edit, old_data), UnicodeString(strip=True, min=1, not_empty=True))
336 password = All(UnicodeString(strip=True, min=6, not_empty=True))
339 password = All(UnicodeString(strip=True, min=6, not_empty=True))
337 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
340 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
338 active = StringBoolean(if_missing=False)
341 active = StringBoolean(if_missing=False)
339 name = UnicodeString(strip=True, min=1, not_empty=True)
342 name = UnicodeString(strip=True, min=1, not_empty=True)
340 lastname = UnicodeString(strip=True, min=1, not_empty=True)
343 lastname = UnicodeString(strip=True, min=1, not_empty=True)
341 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
344 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
342
345
343 chained_validators = [ValidPasswordsMatch, ValidPassword]
346 chained_validators = [ValidPasswordsMatch, ValidPassword]
344
347
345 return _RegisterForm
348 return _RegisterForm
346
349
347 def PasswordResetForm():
350 def PasswordResetForm():
348 class _PasswordResetForm(formencode.Schema):
351 class _PasswordResetForm(formencode.Schema):
349 allow_extra_fields = True
352 allow_extra_fields = True
350 filter_extra_fields = True
353 filter_extra_fields = True
351 email = All(ValidSystemEmail(), Email(not_empty=True))
354 email = All(ValidSystemEmail(), Email(not_empty=True))
352 return _PasswordResetForm
355 return _PasswordResetForm
353
356
354 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
357 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
355 class _RepoForm(formencode.Schema):
358 class _RepoForm(formencode.Schema):
356 allow_extra_fields = True
359 allow_extra_fields = True
357 filter_extra_fields = False
360 filter_extra_fields = False
358 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
361 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
359 description = UnicodeString(strip=True, min=1, not_empty=True)
362 description = UnicodeString(strip=True, min=1, not_empty=True)
360 private = StringBoolean(if_missing=False)
363 private = StringBoolean(if_missing=False)
361 repo_type = OneOf(supported_backends)
364 repo_type = OneOf(supported_backends)
362 if edit:
365 if edit:
363 user = All(Int(not_empty=True), ValidRepoUser)
366 user = All(Int(not_empty=True), ValidRepoUser)
364
367
365 chained_validators = [ValidPerms]
368 chained_validators = [ValidPerms]
366 return _RepoForm
369 return _RepoForm
367
370
368 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
371 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
369 class _RepoForkForm(formencode.Schema):
372 class _RepoForkForm(formencode.Schema):
370 allow_extra_fields = True
373 allow_extra_fields = True
371 filter_extra_fields = False
374 filter_extra_fields = False
372 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
375 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
373 description = UnicodeString(strip=True, min=1, not_empty=True)
376 description = UnicodeString(strip=True, min=1, not_empty=True)
374 private = StringBoolean(if_missing=False)
377 private = StringBoolean(if_missing=False)
375 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
378 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
376 return _RepoForkForm
379 return _RepoForkForm
377
380
378 def RepoSettingsForm(edit=False, old_data={}):
381 def RepoSettingsForm(edit=False, old_data={}):
379 class _RepoForm(formencode.Schema):
382 class _RepoForm(formencode.Schema):
380 allow_extra_fields = True
383 allow_extra_fields = True
381 filter_extra_fields = False
384 filter_extra_fields = False
382 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
385 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
383 description = UnicodeString(strip=True, min=1, not_empty=True)
386 description = UnicodeString(strip=True, min=1, not_empty=True)
384 private = StringBoolean(if_missing=False)
387 private = StringBoolean(if_missing=False)
385
388
386 chained_validators = [ValidPerms, ValidSettings]
389 chained_validators = [ValidPerms, ValidSettings]
387 return _RepoForm
390 return _RepoForm
388
391
389
392
390 def ApplicationSettingsForm():
393 def ApplicationSettingsForm():
391 class _ApplicationSettingsForm(formencode.Schema):
394 class _ApplicationSettingsForm(formencode.Schema):
392 allow_extra_fields = True
395 allow_extra_fields = True
393 filter_extra_fields = False
396 filter_extra_fields = False
394 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
397 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
395 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
398 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
396
399
397 return _ApplicationSettingsForm
400 return _ApplicationSettingsForm
398
401
399 def ApplicationUiSettingsForm():
402 def ApplicationUiSettingsForm():
400 class _ApplicationUiSettingsForm(formencode.Schema):
403 class _ApplicationUiSettingsForm(formencode.Schema):
401 allow_extra_fields = True
404 allow_extra_fields = True
402 filter_extra_fields = False
405 filter_extra_fields = False
403 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
406 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
404 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
407 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
405 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
408 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
406 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
409 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
407 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
410 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
408 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
411 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
409
412
410 return _ApplicationUiSettingsForm
413 return _ApplicationUiSettingsForm
411
414
412 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
415 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
413 class _DefaultPermissionsForm(formencode.Schema):
416 class _DefaultPermissionsForm(formencode.Schema):
414 allow_extra_fields = True
417 allow_extra_fields = True
415 filter_extra_fields = True
418 filter_extra_fields = True
416 overwrite_default = StringBoolean(if_missing=False)
419 overwrite_default = StringBoolean(if_missing=False)
417 anonymous = OneOf(['True', 'False'], if_missing=False)
420 anonymous = OneOf(['True', 'False'], if_missing=False)
418 default_perm = OneOf(perms_choices)
421 default_perm = OneOf(perms_choices)
419 default_register = OneOf(register_choices)
422 default_register = OneOf(register_choices)
420 default_create = OneOf(create_choices)
423 default_create = OneOf(create_choices)
421
424
422 return _DefaultPermissionsForm
425 return _DefaultPermissionsForm
423
426
424
427
425 def LdapSettingsForm():
428 def LdapSettingsForm():
426 class _LdapSettingsForm(formencode.Schema):
429 class _LdapSettingsForm(formencode.Schema):
427 allow_extra_fields = True
430 allow_extra_fields = True
428 filter_extra_fields = True
431 filter_extra_fields = True
429 pre_validators = [LdapLibValidator]
432 pre_validators = [LdapLibValidator]
430 ldap_active = StringBoolean(if_missing=False)
433 ldap_active = StringBoolean(if_missing=False)
431 ldap_host = UnicodeString(strip=True,)
434 ldap_host = UnicodeString(strip=True,)
432 ldap_port = Number(strip=True,)
435 ldap_port = Number(strip=True,)
433 ldap_ldaps = StringBoolean(if_missing=False)
436 ldap_ldaps = StringBoolean(if_missing=False)
434 ldap_dn_user = UnicodeString(strip=True,)
437 ldap_dn_user = UnicodeString(strip=True,)
435 ldap_dn_pass = UnicodeString(strip=True,)
438 ldap_dn_pass = UnicodeString(strip=True,)
436 ldap_base_dn = UnicodeString(strip=True,)
439 ldap_base_dn = UnicodeString(strip=True,)
437
440
438 return _LdapSettingsForm
441 return _LdapSettingsForm
General Comments 0
You need to be logged in to leave comments. Login now