##// END OF EJS Templates
fixed http/s validation for clone_uri, and missing return value.
marcink -
r1298:e3deb588 beta
parent child Browse files
Show More
@@ -1,579 +1,584 b''
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 import traceback
25 26
26 27 import formencode
27 28 from formencode import All
28 29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 30 Email, Bool, StringBoolean, Set
30 31
31 32 from pylons.i18n.translation import _
32 33 from webhelpers.pylonslib.secure_form import authentication_token
33 34
34 35 from rhodecode.lib.utils import repo_name_slug
35 36 from rhodecode.lib.auth import authenticate, get_crypt_password
36 37 from rhodecode.lib.exceptions import LdapImportError
37 38 from rhodecode.model import meta
38 39 from rhodecode.model.user import UserModel
39 40 from rhodecode.model.repo import RepoModel
40 41 from rhodecode.model.db import User, UsersGroup
41 42 from rhodecode import BACKENDS
42 43
43 44 log = logging.getLogger(__name__)
44 45
45 46 #this is needed to translate the messages using _() in validators
46 47 class State_obj(object):
47 48 _ = staticmethod(_)
48 49
49 50 #==============================================================================
50 51 # VALIDATORS
51 52 #==============================================================================
52 53 class ValidAuthToken(formencode.validators.FancyValidator):
53 54 messages = {'invalid_token':_('Token mismatch')}
54 55
55 56 def validate_python(self, value, state):
56 57
57 58 if value != authentication_token():
58 59 raise formencode.Invalid(self.message('invalid_token', state,
59 60 search_number=value), value, state)
60 61
61 62 def ValidUsername(edit, old_data):
62 63 class _ValidUsername(formencode.validators.FancyValidator):
63 64
64 65 def validate_python(self, value, state):
65 66 if value in ['default', 'new_user']:
66 67 raise formencode.Invalid(_('Invalid username'), value, state)
67 68 #check if user is unique
68 69 old_un = None
69 70 if edit:
70 71 old_un = UserModel().get(old_data.get('user_id')).username
71 72
72 73 if old_un != value or not edit:
73 74 if UserModel().get_by_username(value, cache=False,
74 75 case_insensitive=True):
75 76 raise formencode.Invalid(_('This username already '
76 77 'exists') , value, state)
77 78
78 79 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
79 80 raise formencode.Invalid(_('Username may only contain '
80 81 'alphanumeric characters '
81 82 'underscores, periods or dashes '
82 83 'and must begin with alphanumeric '
83 84 'character'), value, state)
84 85
85 86 return _ValidUsername
86 87
87 88
88 89 def ValidUsersGroup(edit, old_data):
89 90
90 91 class _ValidUsersGroup(formencode.validators.FancyValidator):
91 92
92 93 def validate_python(self, value, state):
93 94 if value in ['default']:
94 95 raise formencode.Invalid(_('Invalid group name'), value, state)
95 96 #check if group is unique
96 97 old_ugname = None
97 98 if edit:
98 99 old_ugname = UsersGroup.get(
99 100 old_data.get('users_group_id')).users_group_name
100 101
101 102 if old_ugname != value or not edit:
102 103 if UsersGroup.get_by_group_name(value, cache=False,
103 104 case_insensitive=True):
104 105 raise formencode.Invalid(_('This users group '
105 106 'already exists') , value,
106 107 state)
107 108
108 109
109 110 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
110 111 raise formencode.Invalid(_('Group name may only contain '
111 112 'alphanumeric characters '
112 113 'underscores, periods or dashes '
113 114 'and must begin with alphanumeric '
114 115 'character'), value, state)
115 116
116 117 return _ValidUsersGroup
117 118
118 119
119 120
120 121 class ValidPassword(formencode.validators.FancyValidator):
121 122
122 123 def to_python(self, value, state):
123 124
124 125 if value:
125 126
126 127 if value.get('password'):
127 128 try:
128 129 value['password'] = get_crypt_password(value['password'])
129 130 except UnicodeEncodeError:
130 131 e_dict = {'password':_('Invalid characters in password')}
131 132 raise formencode.Invalid('', value, state, error_dict=e_dict)
132 133
133 134 if value.get('password_confirmation'):
134 135 try:
135 136 value['password_confirmation'] = \
136 137 get_crypt_password(value['password_confirmation'])
137 138 except UnicodeEncodeError:
138 139 e_dict = {'password_confirmation':_('Invalid characters in password')}
139 140 raise formencode.Invalid('', value, state, error_dict=e_dict)
140 141
141 142 if value.get('new_password'):
142 143 try:
143 144 value['new_password'] = \
144 145 get_crypt_password(value['new_password'])
145 146 except UnicodeEncodeError:
146 147 e_dict = {'new_password':_('Invalid characters in password')}
147 148 raise formencode.Invalid('', value, state, error_dict=e_dict)
148 149
149 150 return value
150 151
151 152 class ValidPasswordsMatch(formencode.validators.FancyValidator):
152 153
153 154 def validate_python(self, value, state):
154 155
155 156 if value['password'] != value['password_confirmation']:
156 157 e_dict = {'password_confirmation':
157 158 _('Password do not match')}
158 159 raise formencode.Invalid('', value, state, error_dict=e_dict)
159 160
160 161 class ValidAuth(formencode.validators.FancyValidator):
161 162 messages = {
162 163 'invalid_password':_('invalid password'),
163 164 'invalid_login':_('invalid user name'),
164 165 'disabled_account':_('Your account is disabled')
165 166
166 167 }
167 168 #error mapping
168 169 e_dict = {'username':messages['invalid_login'],
169 170 'password':messages['invalid_password']}
170 171 e_dict_disable = {'username':messages['disabled_account']}
171 172
172 173 def validate_python(self, value, state):
173 174 password = value['password']
174 175 username = value['username']
175 176 user = UserModel().get_by_username(username)
176 177
177 178 if authenticate(username, password):
178 179 return value
179 180 else:
180 181 if user and user.active is False:
181 182 log.warning('user %s is disabled', username)
182 183 raise formencode.Invalid(self.message('disabled_account',
183 184 state=State_obj),
184 185 value, state,
185 186 error_dict=self.e_dict_disable)
186 187 else:
187 188 log.warning('user %s not authenticated', username)
188 189 raise formencode.Invalid(self.message('invalid_password',
189 190 state=State_obj), value, state,
190 191 error_dict=self.e_dict)
191 192
192 193 class ValidRepoUser(formencode.validators.FancyValidator):
193 194
194 195 def to_python(self, value, state):
195 196 sa = meta.Session()
196 197 try:
197 198 self.user_db = sa.query(User)\
198 199 .filter(User.active == True)\
199 200 .filter(User.username == value).one()
200 201 except Exception:
201 202 raise formencode.Invalid(_('This username is not valid'),
202 203 value, state)
203 204 finally:
204 205 meta.Session.remove()
205 206
206 207 return self.user_db.user_id
207 208
208 209 def ValidRepoName(edit, old_data):
209 210 class _ValidRepoName(formencode.validators.FancyValidator):
210 211
211 212 def to_python(self, value, state):
212 213 slug = repo_name_slug(value)
213 214 if slug in ['_admin']:
214 215 raise formencode.Invalid(_('This repository name is disallowed'),
215 216 value, state)
216 217 if old_data.get('repo_name') != value or not edit:
217 218 if RepoModel().get_by_repo_name(slug, cache=False):
218 219 raise formencode.Invalid(_('This repository already exists') ,
219 220 value, state)
220 221 return slug
221 222
222 223
223 224 return _ValidRepoName
224 225
225 226 def ValidCloneUri():
226 227 from mercurial.httprepo import httprepository, httpsrepository
227 228 from rhodecode.lib.utils import make_ui
228 229
229 230 class _ValidCloneUri(formencode.validators.FancyValidator):
231
230 232 def to_python(self, value, state):
231 233 if not value:
232 234 pass
233 235 elif value.startswith('https'):
234 236 try:
235 httpsrepository(make_ui('db'), value).capabilities()
236 except:
237 httpsrepository(make_ui('db'), value).capabilities
238 except Exception, e:
239 log.error(traceback.format_exc())
237 240 raise formencode.Invalid(_('invalid clone url'), value,
238 241 state)
239 242 elif value.startswith('http'):
240 243 try:
241 httprepository(make_ui('db'), value).capabilities()
242 except:
244 httprepository(make_ui('db'), value).capabilities
245 except Exception, e:
246 log.error(traceback.format_exc())
243 247 raise formencode.Invalid(_('invalid clone url'), value,
244 248 state)
245 249 else:
246 250 raise formencode.Invalid(_('Invalid clone url, provide a '
247 251 'valid clone http\s url'), value,
248 252 state)
253 return value
249 254
250 255 return _ValidCloneUri
251 256
252 257 def ValidForkType(old_data):
253 258 class _ValidForkType(formencode.validators.FancyValidator):
254 259
255 260 def to_python(self, value, state):
256 261 if old_data['repo_type'] != value:
257 262 raise formencode.Invalid(_('Fork have to be the same '
258 263 'type as original'), value, state)
259 264 return value
260 265 return _ValidForkType
261 266
262 267 class ValidPerms(formencode.validators.FancyValidator):
263 268 messages = {'perm_new_member_name':_('This username or users group name'
264 269 ' is not valid')}
265 270
266 271 def to_python(self, value, state):
267 272 perms_update = []
268 273 perms_new = []
269 274 #build a list of permission to update and new permission to create
270 275 for k, v in value.items():
271 276 #means new added member to permissions
272 277 if k.startswith('perm_new_member'):
273 278 new_perm = value.get('perm_new_member', False)
274 279 new_member = value.get('perm_new_member_name', False)
275 280 new_type = value.get('perm_new_member_type')
276 281
277 282 if new_member and new_perm:
278 283 if (new_member, new_perm, new_type) not in perms_new:
279 284 perms_new.append((new_member, new_perm, new_type))
280 285 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
281 286 member = k[7:]
282 287 t = {'u':'user',
283 288 'g':'users_group'}[k[0]]
284 289 if member == 'default':
285 290 if value['private']:
286 291 #set none for default when updating to private repo
287 292 v = 'repository.none'
288 293 perms_update.append((member, v, t))
289 294
290 295 value['perms_updates'] = perms_update
291 296 value['perms_new'] = perms_new
292 297
293 298 #update permissions
294 299 sa = meta.Session
295 300 for k, v, t in perms_new:
296 301 try:
297 302 if t is 'user':
298 303 self.user_db = sa.query(User)\
299 304 .filter(User.active == True)\
300 305 .filter(User.username == k).one()
301 306 if t is 'users_group':
302 307 self.user_db = sa.query(UsersGroup)\
303 308 .filter(UsersGroup.users_group_active == True)\
304 309 .filter(UsersGroup.users_group_name == k).one()
305 310
306 311 except Exception:
307 312 msg = self.message('perm_new_member_name',
308 313 state=State_obj)
309 314 raise formencode.Invalid(msg, value, state,
310 315 error_dict={'perm_new_member_name':msg})
311 316 return value
312 317
313 318 class ValidSettings(formencode.validators.FancyValidator):
314 319
315 320 def to_python(self, value, state):
316 321 #settings form can't edit user
317 322 if value.has_key('user'):
318 323 del['value']['user']
319 324
320 325 return value
321 326
322 327 class ValidPath(formencode.validators.FancyValidator):
323 328 def to_python(self, value, state):
324 329
325 330 if not os.path.isdir(value):
326 331 msg = _('This is not a valid path')
327 332 raise formencode.Invalid(msg, value, state,
328 333 error_dict={'paths_root_path':msg})
329 334 return value
330 335
331 336 def UniqSystemEmail(old_data):
332 337 class _UniqSystemEmail(formencode.validators.FancyValidator):
333 338 def to_python(self, value, state):
334 339 value = value.lower()
335 340 if old_data.get('email') != value:
336 341 sa = meta.Session()
337 342 try:
338 343 user = sa.query(User).filter(User.email == value).scalar()
339 344 if user:
340 345 raise formencode.Invalid(_("This e-mail address is already taken") ,
341 346 value, state)
342 347 finally:
343 348 meta.Session.remove()
344 349
345 350 return value
346 351
347 352 return _UniqSystemEmail
348 353
349 354 class ValidSystemEmail(formencode.validators.FancyValidator):
350 355 def to_python(self, value, state):
351 356 value = value.lower()
352 357 sa = meta.Session
353 358 try:
354 359 user = sa.query(User).filter(User.email == value).scalar()
355 360 if user is None:
356 361 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
357 362 value, state)
358 363 finally:
359 364 meta.Session.remove()
360 365
361 366 return value
362 367
363 368 class LdapLibValidator(formencode.validators.FancyValidator):
364 369
365 370 def to_python(self, value, state):
366 371
367 372 try:
368 373 import ldap
369 374 except ImportError:
370 375 raise LdapImportError
371 376 return value
372 377
373 378 class AttrLoginValidator(formencode.validators.FancyValidator):
374 379
375 380 def to_python(self, value, state):
376 381
377 382 if not value or not isinstance(value, (str, unicode)):
378 383 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
379 384 "must be specified - this is the name "
380 385 "of the attribute that is equivalent "
381 386 "to 'username'"),
382 387 value, state)
383 388
384 389 return value
385 390
386 391 #===============================================================================
387 392 # FORMS
388 393 #===============================================================================
389 394 class LoginForm(formencode.Schema):
390 395 allow_extra_fields = True
391 396 filter_extra_fields = True
392 397 username = UnicodeString(
393 398 strip=True,
394 399 min=1,
395 400 not_empty=True,
396 401 messages={
397 402 'empty':_('Please enter a login'),
398 403 'tooShort':_('Enter a value %(min)i characters long or more')}
399 404 )
400 405
401 406 password = UnicodeString(
402 407 strip=True,
403 408 min=6,
404 409 not_empty=True,
405 410 messages={
406 411 'empty':_('Please enter a password'),
407 412 'tooShort':_('Enter %(min)i characters or more')}
408 413 )
409 414
410 415
411 416 #chained validators have access to all data
412 417 chained_validators = [ValidAuth]
413 418
414 419 def UserForm(edit=False, old_data={}):
415 420 class _UserForm(formencode.Schema):
416 421 allow_extra_fields = True
417 422 filter_extra_fields = True
418 423 username = All(UnicodeString(strip=True, min=1, not_empty=True),
419 424 ValidUsername(edit, old_data))
420 425 if edit:
421 426 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
422 427 admin = StringBoolean(if_missing=False)
423 428 else:
424 429 password = All(UnicodeString(strip=True, min=6, not_empty=True))
425 430 active = StringBoolean(if_missing=False)
426 431 name = UnicodeString(strip=True, min=1, not_empty=True)
427 432 lastname = UnicodeString(strip=True, min=1, not_empty=True)
428 433 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
429 434
430 435 chained_validators = [ValidPassword]
431 436
432 437 return _UserForm
433 438
434 439
435 440 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
436 441 class _UsersGroupForm(formencode.Schema):
437 442 allow_extra_fields = True
438 443 filter_extra_fields = True
439 444
440 445 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
441 446 ValidUsersGroup(edit, old_data))
442 447
443 448 users_group_active = StringBoolean(if_missing=False)
444 449
445 450 if edit:
446 451 users_group_members = OneOf(available_members, hideList=False,
447 452 testValueList=True,
448 453 if_missing=None, not_empty=False)
449 454
450 455 return _UsersGroupForm
451 456
452 457 def RegisterForm(edit=False, old_data={}):
453 458 class _RegisterForm(formencode.Schema):
454 459 allow_extra_fields = True
455 460 filter_extra_fields = True
456 461 username = All(ValidUsername(edit, old_data),
457 462 UnicodeString(strip=True, min=1, not_empty=True))
458 463 password = All(UnicodeString(strip=True, min=6, not_empty=True))
459 464 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
460 465 active = StringBoolean(if_missing=False)
461 466 name = UnicodeString(strip=True, min=1, not_empty=True)
462 467 lastname = UnicodeString(strip=True, min=1, not_empty=True)
463 468 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
464 469
465 470 chained_validators = [ValidPasswordsMatch, ValidPassword]
466 471
467 472 return _RegisterForm
468 473
469 474 def PasswordResetForm():
470 475 class _PasswordResetForm(formencode.Schema):
471 476 allow_extra_fields = True
472 477 filter_extra_fields = True
473 478 email = All(ValidSystemEmail(), Email(not_empty=True))
474 479 return _PasswordResetForm
475 480
476 481 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
477 482 repo_groups=[]):
478 483 class _RepoForm(formencode.Schema):
479 484 allow_extra_fields = True
480 485 filter_extra_fields = False
481 486 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
482 487 ValidRepoName(edit, old_data))
483 488 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
484 489 ValidCloneUri()())
485 490 repo_group = OneOf(repo_groups, hideList=True)
486 491 repo_type = OneOf(supported_backends)
487 492 description = UnicodeString(strip=True, min=1, not_empty=True)
488 493 private = StringBoolean(if_missing=False)
489 494 enable_statistics = StringBoolean(if_missing=False)
490 495 enable_downloads = StringBoolean(if_missing=False)
491 496
492 497 if edit:
493 498 #this is repo owner
494 499 user = All(Int(not_empty=True), ValidRepoUser)
495 500
496 501 chained_validators = [ValidPerms]
497 502 return _RepoForm
498 503
499 504 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
500 505 class _RepoForkForm(formencode.Schema):
501 506 allow_extra_fields = True
502 507 filter_extra_fields = False
503 508 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
504 509 ValidRepoName(edit, old_data))
505 510 description = UnicodeString(strip=True, min=1, not_empty=True)
506 511 private = StringBoolean(if_missing=False)
507 512 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
508 513 return _RepoForkForm
509 514
510 515 def RepoSettingsForm(edit=False, old_data={}):
511 516 class _RepoForm(formencode.Schema):
512 517 allow_extra_fields = True
513 518 filter_extra_fields = False
514 519 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
515 520 ValidRepoName(edit, old_data))
516 521 description = UnicodeString(strip=True, min=1, not_empty=True)
517 522 private = StringBoolean(if_missing=False)
518 523
519 524 chained_validators = [ValidPerms, ValidSettings]
520 525 return _RepoForm
521 526
522 527
523 528 def ApplicationSettingsForm():
524 529 class _ApplicationSettingsForm(formencode.Schema):
525 530 allow_extra_fields = True
526 531 filter_extra_fields = False
527 532 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
528 533 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
529 534 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
530 535
531 536 return _ApplicationSettingsForm
532 537
533 538 def ApplicationUiSettingsForm():
534 539 class _ApplicationUiSettingsForm(formencode.Schema):
535 540 allow_extra_fields = True
536 541 filter_extra_fields = False
537 542 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
538 543 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
539 544 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
540 545 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
541 546 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
542 547 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
543 548
544 549 return _ApplicationUiSettingsForm
545 550
546 551 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
547 552 class _DefaultPermissionsForm(formencode.Schema):
548 553 allow_extra_fields = True
549 554 filter_extra_fields = True
550 555 overwrite_default = StringBoolean(if_missing=False)
551 556 anonymous = OneOf(['True', 'False'], if_missing=False)
552 557 default_perm = OneOf(perms_choices)
553 558 default_register = OneOf(register_choices)
554 559 default_create = OneOf(create_choices)
555 560
556 561 return _DefaultPermissionsForm
557 562
558 563
559 564 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
560 565 class _LdapSettingsForm(formencode.Schema):
561 566 allow_extra_fields = True
562 567 filter_extra_fields = True
563 568 pre_validators = [LdapLibValidator]
564 569 ldap_active = StringBoolean(if_missing=False)
565 570 ldap_host = UnicodeString(strip=True,)
566 571 ldap_port = Number(strip=True,)
567 572 ldap_tls_kind = OneOf(tls_kind_choices)
568 573 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
569 574 ldap_dn_user = UnicodeString(strip=True,)
570 575 ldap_dn_pass = UnicodeString(strip=True,)
571 576 ldap_base_dn = UnicodeString(strip=True,)
572 577 ldap_filter = UnicodeString(strip=True,)
573 578 ldap_search_scope = OneOf(search_scope_choices)
574 579 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
575 580 ldap_attr_firstname = UnicodeString(strip=True,)
576 581 ldap_attr_lastname = UnicodeString(strip=True,)
577 582 ldap_attr_email = UnicodeString(strip=True,)
578 583
579 584 return _LdapSettingsForm
General Comments 0
You need to be logged in to leave comments. Login now