##// END OF EJS Templates
fixed ldap form, it doesn't validate the baseDN until enable checkbox is set
marcink -
r1145:9a994632 default
parent child Browse files
Show More
@@ -1,105 +1,106
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.ldap_settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 ldap controller for RhodeCode
7 7
8 8 :created_on: Nov 26, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27 import logging
28 28 import formencode
29 29 import traceback
30 30
31 31 from formencode import htmlfill
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode.lib.base import BaseController, render
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 40 from rhodecode.lib.auth_ldap import LdapImportError
41 41 from rhodecode.model.settings import SettingsModel
42 42 from rhodecode.model.forms import LdapSettingsForm
43 43 from sqlalchemy.exc import DatabaseError
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48
49 49 class LdapSettingsController(BaseController):
50 50
51 51 @LoginRequired()
52 52 @HasPermissionAllDecorator('hg.admin')
53 53 def __before__(self):
54 54 c.admin_user = session.get('admin_user')
55 55 c.admin_username = session.get('admin_username')
56 56 super(LdapSettingsController, self).__before__()
57 57
58 58 def index(self):
59 59 defaults = SettingsModel().get_ldap_settings()
60 60
61 61 return htmlfill.render(
62 62 render('admin/ldap/ldap.html'),
63 63 defaults=defaults,
64 64 encoding="UTF-8",
65 65 force_defaults=True,)
66 66
67 67 def ldap_settings(self):
68 68 """POST ldap create and store ldap settings"""
69 69
70 70 settings_model = SettingsModel()
71 _form = LdapSettingsForm()()
71 post_data = dict(request.POST)
72 _form = LdapSettingsForm(post_data.get('ldap_active'))()
72 73
73 74 try:
74 form_result = _form.to_python(dict(request.POST))
75 form_result = _form.to_python(post_data)
75 76 try:
76 77
77 78 for k, v in form_result.items():
78 79 if k.startswith('ldap_'):
79 80 setting = settings_model.get(k)
80 81 setting.app_settings_value = v
81 82 self.sa.add(setting)
82 83
83 84 self.sa.commit()
84 85 h.flash(_('Ldap settings updated successfully'),
85 86 category='success')
86 87 except (DatabaseError,):
87 88 raise
88 89 except LdapImportError:
89 90 h.flash(_('Unable to activate ldap. The "python-ldap" library '
90 91 'is missing.'), category='warning')
91 92
92 93 except formencode.Invalid, errors:
93 94
94 95 return htmlfill.render(
95 96 render('admin/ldap/ldap.html'),
96 97 defaults=errors.value,
97 98 errors=errors.error_dict or {},
98 99 prefix_error=False,
99 100 encoding="UTF-8")
100 101 except Exception:
101 102 log.error(traceback.format_exc())
102 103 h.flash(_('error occurred during update of ldap settings'),
103 104 category='error')
104 105
105 106 return redirect(url('ldap_home'))
@@ -1,483 +1,486
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 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 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 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 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 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25
26 26 import formencode
27 27 from formencode import All
28 28 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 29 Email, Bool, StringBoolean
30 30
31 31 from pylons.i18n.translation import _
32 32
33 33 import rhodecode.lib.helpers as h
34 34 from rhodecode.lib.auth import authenticate, get_crypt_password
35 35 from rhodecode.lib.exceptions import LdapImportError
36 36 from rhodecode.model import meta
37 37 from rhodecode.model.user import UserModel
38 38 from rhodecode.model.repo import RepoModel
39 39 from rhodecode.model.db import User
40 40 from rhodecode import BACKENDS
41 41
42 42 from webhelpers.pylonslib.secure_form import authentication_token
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 #this is needed to translate the messages using _() in validators
47 47 class State_obj(object):
48 48 _ = staticmethod(_)
49 49
50 50 #===============================================================================
51 51 # VALIDATORS
52 52 #===============================================================================
53 53 class ValidAuthToken(formencode.validators.FancyValidator):
54 54 messages = {'invalid_token':_('Token mismatch')}
55 55
56 56 def validate_python(self, value, state):
57 57
58 58 if value != authentication_token():
59 59 raise formencode.Invalid(self.message('invalid_token', state,
60 60 search_number=value), value, state)
61 61
62 62 def ValidUsername(edit, old_data):
63 63 class _ValidUsername(formencode.validators.FancyValidator):
64 64
65 65 def validate_python(self, value, state):
66 66 if value in ['default', 'new_user']:
67 67 raise formencode.Invalid(_('Invalid username'), value, state)
68 68 #check if user is unique
69 69 old_un = None
70 70 if edit:
71 71 old_un = UserModel().get(old_data.get('user_id')).username
72 72
73 73 if old_un != value or not edit:
74 74 if UserModel().get_by_username(value, cache=False,
75 75 case_insensitive=True):
76 76 raise formencode.Invalid(_('This username already exists') ,
77 77 value, state)
78 78
79 79
80 80 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
81 81 raise formencode.Invalid(_('Username may only contain '
82 82 'alphanumeric characters underscores, '
83 83 'periods or dashes and must begin with '
84 84 'alphanumeric character'),
85 85 value, state)
86 86
87 87
88 88
89 89 return _ValidUsername
90 90
91 91 class ValidPassword(formencode.validators.FancyValidator):
92 92
93 93 def to_python(self, value, state):
94 94
95 95 if value:
96 96
97 97 if value.get('password'):
98 98 try:
99 99 value['password'] = get_crypt_password(value['password'])
100 100 except UnicodeEncodeError:
101 101 e_dict = {'password':_('Invalid characters in password')}
102 102 raise formencode.Invalid('', value, state, error_dict=e_dict)
103 103
104 104 if value.get('password_confirmation'):
105 105 try:
106 106 value['password_confirmation'] = \
107 107 get_crypt_password(value['password_confirmation'])
108 108 except UnicodeEncodeError:
109 109 e_dict = {'password_confirmation':_('Invalid characters in password')}
110 110 raise formencode.Invalid('', value, state, error_dict=e_dict)
111 111
112 112 if value.get('new_password'):
113 113 try:
114 114 value['new_password'] = \
115 115 get_crypt_password(value['new_password'])
116 116 except UnicodeEncodeError:
117 117 e_dict = {'new_password':_('Invalid characters in password')}
118 118 raise formencode.Invalid('', value, state, error_dict=e_dict)
119 119
120 120 return value
121 121
122 122 class ValidPasswordsMatch(formencode.validators.FancyValidator):
123 123
124 124 def validate_python(self, value, state):
125 125
126 126 if value['password'] != value['password_confirmation']:
127 127 e_dict = {'password_confirmation':
128 128 _('Password do not match')}
129 129 raise formencode.Invalid('', value, state, error_dict=e_dict)
130 130
131 131 class ValidAuth(formencode.validators.FancyValidator):
132 132 messages = {
133 133 'invalid_password':_('invalid password'),
134 134 'invalid_login':_('invalid user name'),
135 135 'disabled_account':_('Your account is disabled')
136 136
137 137 }
138 138 #error mapping
139 139 e_dict = {'username':messages['invalid_login'],
140 140 'password':messages['invalid_password']}
141 141 e_dict_disable = {'username':messages['disabled_account']}
142 142
143 143 def validate_python(self, value, state):
144 144 password = value['password']
145 145 username = value['username']
146 146 user = UserModel().get_by_username(username)
147 147
148 148 if authenticate(username, password):
149 149 return value
150 150 else:
151 151 if user and user.active is False:
152 152 log.warning('user %s is disabled', username)
153 153 raise formencode.Invalid(self.message('disabled_account',
154 154 state=State_obj),
155 155 value, state,
156 156 error_dict=self.e_dict_disable)
157 157 else:
158 158 log.warning('user %s not authenticated', username)
159 159 raise formencode.Invalid(self.message('invalid_password',
160 160 state=State_obj), value, state,
161 161 error_dict=self.e_dict)
162 162
163 163 class ValidRepoUser(formencode.validators.FancyValidator):
164 164
165 165 def to_python(self, value, state):
166 166 sa = meta.Session()
167 167 try:
168 168 self.user_db = sa.query(User)\
169 169 .filter(User.active == True)\
170 170 .filter(User.username == value).one()
171 171 except Exception:
172 172 raise formencode.Invalid(_('This username is not valid'),
173 173 value, state)
174 174 finally:
175 175 meta.Session.remove()
176 176
177 177 return self.user_db.user_id
178 178
179 179 def ValidRepoName(edit, old_data):
180 180 class _ValidRepoName(formencode.validators.FancyValidator):
181 181
182 182 def to_python(self, value, state):
183 183 slug = h.repo_name_slug(value)
184 184 if slug in ['_admin']:
185 185 raise formencode.Invalid(_('This repository name is disallowed'),
186 186 value, state)
187 187 if old_data.get('repo_name') != value or not edit:
188 188 if RepoModel().get_by_repo_name(slug, cache=False):
189 189 raise formencode.Invalid(_('This repository already exists') ,
190 190 value, state)
191 191 return slug
192 192
193 193
194 194 return _ValidRepoName
195 195
196 196 def ValidForkType(old_data):
197 197 class _ValidForkType(formencode.validators.FancyValidator):
198 198
199 199 def to_python(self, value, state):
200 200 if old_data['repo_type'] != value:
201 201 raise formencode.Invalid(_('Fork have to be the same type as original'),
202 202 value, state)
203 203 return value
204 204 return _ValidForkType
205 205
206 206 class ValidPerms(formencode.validators.FancyValidator):
207 207 messages = {'perm_new_user_name':_('This username is not valid')}
208 208
209 209 def to_python(self, value, state):
210 210 perms_update = []
211 211 perms_new = []
212 212 #build a list of permission to update and new permission to create
213 213 for k, v in value.items():
214 214 if k.startswith('perm_'):
215 215 if k.startswith('perm_new_user'):
216 216 new_perm = value.get('perm_new_user', False)
217 217 new_user = value.get('perm_new_user_name', False)
218 218 if new_user and new_perm:
219 219 if (new_user, new_perm) not in perms_new:
220 220 perms_new.append((new_user, new_perm))
221 221 else:
222 222 usr = k[5:]
223 223 if usr == 'default':
224 224 if value['private']:
225 225 #set none for default when updating to private repo
226 226 v = 'repository.none'
227 227 perms_update.append((usr, v))
228 228 value['perms_updates'] = perms_update
229 229 value['perms_new'] = perms_new
230 230 sa = meta.Session
231 231 for k, v in perms_new:
232 232 try:
233 233 self.user_db = sa.query(User)\
234 234 .filter(User.active == True)\
235 235 .filter(User.username == k).one()
236 236 except Exception:
237 237 msg = self.message('perm_new_user_name',
238 238 state=State_obj)
239 239 raise formencode.Invalid(msg, value, state,
240 240 error_dict={'perm_new_user_name':msg})
241 241 return value
242 242
243 243 class ValidSettings(formencode.validators.FancyValidator):
244 244
245 245 def to_python(self, value, state):
246 246 #settings form can't edit user
247 247 if value.has_key('user'):
248 248 del['value']['user']
249 249
250 250 return value
251 251
252 252 class ValidPath(formencode.validators.FancyValidator):
253 253 def to_python(self, value, state):
254 254
255 255 if not os.path.isdir(value):
256 256 msg = _('This is not a valid path')
257 257 raise formencode.Invalid(msg, value, state,
258 258 error_dict={'paths_root_path':msg})
259 259 return value
260 260
261 261 def UniqSystemEmail(old_data):
262 262 class _UniqSystemEmail(formencode.validators.FancyValidator):
263 263 def to_python(self, value, state):
264 264 value = value.lower()
265 265 if old_data.get('email') != value:
266 266 sa = meta.Session()
267 267 try:
268 268 user = sa.query(User).filter(User.email == value).scalar()
269 269 if user:
270 270 raise formencode.Invalid(_("This e-mail address is already taken") ,
271 271 value, state)
272 272 finally:
273 273 meta.Session.remove()
274 274
275 275 return value
276 276
277 277 return _UniqSystemEmail
278 278
279 279 class ValidSystemEmail(formencode.validators.FancyValidator):
280 280 def to_python(self, value, state):
281 281 value = value.lower()
282 282 sa = meta.Session
283 283 try:
284 284 user = sa.query(User).filter(User.email == value).scalar()
285 285 if user is None:
286 286 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
287 287 value, state)
288 288 finally:
289 289 meta.Session.remove()
290 290
291 291 return value
292 292
293 293 class LdapLibValidator(formencode.validators.FancyValidator):
294 294
295 295 def to_python(self, value, state):
296 296
297 297 try:
298 298 import ldap
299 299 except ImportError:
300 300 raise LdapImportError
301 301 return value
302 302
303 class BaseDnValidator(formencode.validators.FancyValidator):
303 def BaseDnValidator(ldap_enable):
304 class _BaseDnValidator(formencode.validators.FancyValidator):
305
306 def to_python(self, value, state):
304 307
305 def to_python(self, value, state):
306
307 try:
308 value % {'user':'valid'}
308 if not ldap_enable:
309 return ''
310 try:
311 value % {'user':'valid'}
309 312
310 if value.find('%(user)s') == -1:
311 raise formencode.Invalid(_("You need to specify %(user)s in "
312 "template for example uid=%(user)s "
313 ",dc=company...") ,
314 value, state)
313 if value.find('%(user)s') == -1:
314 raise formencode.Invalid(_("You need to specify %(user)s in "
315 "template for example uid=%(user)s "
316 ",dc=company...") ,
317 value, state)
315 318
316 except KeyError:
317 raise formencode.Invalid(_("Wrong template used, only %(user)s "
318 "is an valid entry") ,
319 value, state)
319 except KeyError:
320 raise formencode.Invalid(_("Wrong template used, only %(user)s "
321 "is an valid entry") ,
322 value, state)
320 323
321 return value
322
324 return value
325 return _BaseDnValidator
323 326 #===============================================================================
324 327 # FORMS
325 328 #===============================================================================
326 329 class LoginForm(formencode.Schema):
327 330 allow_extra_fields = True
328 331 filter_extra_fields = True
329 332 username = UnicodeString(
330 333 strip=True,
331 334 min=1,
332 335 not_empty=True,
333 336 messages={
334 337 'empty':_('Please enter a login'),
335 338 'tooShort':_('Enter a value %(min)i characters long or more')}
336 339 )
337 340
338 341 password = UnicodeString(
339 342 strip=True,
340 343 min=6,
341 344 not_empty=True,
342 345 messages={
343 346 'empty':_('Please enter a password'),
344 347 'tooShort':_('Enter %(min)i characters or more')}
345 348 )
346 349
347 350
348 351 #chained validators have access to all data
349 352 chained_validators = [ValidAuth]
350 353
351 354 def UserForm(edit=False, old_data={}):
352 355 class _UserForm(formencode.Schema):
353 356 allow_extra_fields = True
354 357 filter_extra_fields = True
355 358 username = All(UnicodeString(strip=True, min=1, not_empty=True),
356 359 ValidUsername(edit, old_data))
357 360 if edit:
358 361 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
359 362 admin = StringBoolean(if_missing=False)
360 363 else:
361 364 password = All(UnicodeString(strip=True, min=6, not_empty=True))
362 365 active = StringBoolean(if_missing=False)
363 366 name = UnicodeString(strip=True, min=1, not_empty=True)
364 367 lastname = UnicodeString(strip=True, min=1, not_empty=True)
365 368 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
366 369
367 370 chained_validators = [ValidPassword]
368 371
369 372 return _UserForm
370 373
371 374 def RegisterForm(edit=False, old_data={}):
372 375 class _RegisterForm(formencode.Schema):
373 376 allow_extra_fields = True
374 377 filter_extra_fields = True
375 378 username = All(ValidUsername(edit, old_data),
376 379 UnicodeString(strip=True, min=1, not_empty=True))
377 380 password = All(UnicodeString(strip=True, min=6, not_empty=True))
378 381 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
379 382 active = StringBoolean(if_missing=False)
380 383 name = UnicodeString(strip=True, min=1, not_empty=True)
381 384 lastname = UnicodeString(strip=True, min=1, not_empty=True)
382 385 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
383 386
384 387 chained_validators = [ValidPasswordsMatch, ValidPassword]
385 388
386 389 return _RegisterForm
387 390
388 391 def PasswordResetForm():
389 392 class _PasswordResetForm(formencode.Schema):
390 393 allow_extra_fields = True
391 394 filter_extra_fields = True
392 395 email = All(ValidSystemEmail(), Email(not_empty=True))
393 396 return _PasswordResetForm
394 397
395 398 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
396 399 class _RepoForm(formencode.Schema):
397 400 allow_extra_fields = True
398 401 filter_extra_fields = False
399 402 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
400 403 ValidRepoName(edit, old_data))
401 404 description = UnicodeString(strip=True, min=1, not_empty=True)
402 405 private = StringBoolean(if_missing=False)
403 406 enable_statistics = StringBoolean(if_missing=False)
404 407 repo_type = OneOf(supported_backends)
405 408 if edit:
406 409 user = All(Int(not_empty=True), ValidRepoUser)
407 410
408 411 chained_validators = [ValidPerms]
409 412 return _RepoForm
410 413
411 414 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
412 415 class _RepoForkForm(formencode.Schema):
413 416 allow_extra_fields = True
414 417 filter_extra_fields = False
415 418 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
416 419 ValidRepoName(edit, old_data))
417 420 description = UnicodeString(strip=True, min=1, not_empty=True)
418 421 private = StringBoolean(if_missing=False)
419 422 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
420 423 return _RepoForkForm
421 424
422 425 def RepoSettingsForm(edit=False, old_data={}):
423 426 class _RepoForm(formencode.Schema):
424 427 allow_extra_fields = True
425 428 filter_extra_fields = False
426 429 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
427 430 ValidRepoName(edit, old_data))
428 431 description = UnicodeString(strip=True, min=1, not_empty=True)
429 432 private = StringBoolean(if_missing=False)
430 433
431 434 chained_validators = [ValidPerms, ValidSettings]
432 435 return _RepoForm
433 436
434 437
435 438 def ApplicationSettingsForm():
436 439 class _ApplicationSettingsForm(formencode.Schema):
437 440 allow_extra_fields = True
438 441 filter_extra_fields = False
439 442 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
440 443 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
441 444
442 445 return _ApplicationSettingsForm
443 446
444 447 def ApplicationUiSettingsForm():
445 448 class _ApplicationUiSettingsForm(formencode.Schema):
446 449 allow_extra_fields = True
447 450 filter_extra_fields = False
448 451 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
449 452 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
450 453 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
451 454 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
452 455 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
453 456 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
454 457
455 458 return _ApplicationUiSettingsForm
456 459
457 460 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
458 461 class _DefaultPermissionsForm(formencode.Schema):
459 462 allow_extra_fields = True
460 463 filter_extra_fields = True
461 464 overwrite_default = StringBoolean(if_missing=False)
462 465 anonymous = OneOf(['True', 'False'], if_missing=False)
463 466 default_perm = OneOf(perms_choices)
464 467 default_register = OneOf(register_choices)
465 468 default_create = OneOf(create_choices)
466 469
467 470 return _DefaultPermissionsForm
468 471
469 472
470 def LdapSettingsForm():
473 def LdapSettingsForm(ldap_enable):
471 474 class _LdapSettingsForm(formencode.Schema):
472 475 allow_extra_fields = True
473 476 filter_extra_fields = True
474 477 pre_validators = [LdapLibValidator]
475 478 ldap_active = StringBoolean(if_missing=False)
476 479 ldap_host = UnicodeString(strip=True,)
477 480 ldap_port = Number(strip=True,)
478 481 ldap_ldaps = StringBoolean(if_missing=False)
479 482 ldap_dn_user = UnicodeString(strip=True,)
480 483 ldap_dn_pass = UnicodeString(strip=True,)
481 ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,))
484 ldap_base_dn = All(BaseDnValidator(ldap_enable), UnicodeString(strip=True,))
482 485
483 486 return _LdapSettingsForm
General Comments 0
You need to be logged in to leave comments. Login now