comparison rhodecode/model/validators.py @ 4116:ffd45b185016 rhodecode-2.2.5-gpl

Imported some of the GPLv3'd changes from RhodeCode v2.2.5. This imports changes between changesets 21af6c4eab3d and 6177597791c2 in RhodeCode's original repository, including only changes to Python files and HTML. RhodeCode clearly licensed its changes to these files under GPLv3 in their /LICENSE file, which states the following: The Python code and integrated HTML are licensed under the GPLv3 license. (See: https://code.rhodecode.com/rhodecode/files/v2.2.5/LICENSE or http://web.archive.org/web/20140512193334/https://code.rhodecode.com/rhodecode/files/f3b123159901f15426d18e3dc395e8369f70ebe0/LICENSE for an online copy of that LICENSE file) Conservancy reviewed these changes and confirmed that they can be licensed as a whole to the Kallithea project under GPLv3-only. While some of the contents committed herein are clearly licensed GPLv3-or-later, on the whole we must assume the are GPLv3-only, since the statement above from RhodeCode indicates that they intend GPLv3-only as their license, per GPLv3ยง14 and other relevant sections of GPLv3.
author Bradley M. Kuhn <bkuhn@sfconservancy.org>
date Wed, 02 Jul 2014 19:03:13 -0400
parents 5293d4bbb1ea
children 7e5f8c12a3fc
comparison
equal deleted inserted replaced
4115:8b7294a804a0 4116:ffd45b185016
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1 """ 14 """
2 Set of generic validators 15 Set of generic validators
3 """ 16 """
17
4 import os 18 import os
5 import re 19 import re
6 import formencode 20 import formencode
7 import logging 21 import logging
8 from collections import defaultdict 22 from collections import defaultdict
14 NotEmpty, IPAddress, CIDR, String, FancyValidator 28 NotEmpty, IPAddress, CIDR, String, FancyValidator
15 ) 29 )
16 from rhodecode.lib.compat import OrderedSet 30 from rhodecode.lib.compat import OrderedSet
17 from rhodecode.lib import ipaddr 31 from rhodecode.lib import ipaddr
18 from rhodecode.lib.utils import repo_name_slug 32 from rhodecode.lib.utils import repo_name_slug
19 from rhodecode.lib.utils2 import safe_int, str2bool 33 from rhodecode.lib.utils2 import safe_int, str2bool, aslist
20 from rhodecode.model.db import RepoGroup, Repository, UserGroup, User,\ 34 from rhodecode.model.db import RepoGroup, Repository, UserGroup, User,\
21 ChangesetStatus 35 ChangesetStatus
22 from rhodecode.lib.exceptions import LdapImportError 36 from rhodecode.lib.exceptions import LdapImportError
23 from rhodecode.config.routing import ADMIN_PREFIX 37 from rhodecode.config.routing import ADMIN_PREFIX
24 from rhodecode.lib.auth import HasReposGroupPermissionAny, HasPermissionAny 38 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
25 39
26 # silence warnings and pylint 40 # silence warnings and pylint
27 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \ 41 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
28 NotEmpty, IPAddress, CIDR, String, FancyValidator 42 NotEmpty, IPAddress, CIDR, String, FancyValidator
29 43
30 log = logging.getLogger(__name__) 44 log = logging.getLogger(__name__)
31 45
32 46 class _Missing(object):
33 class UniqueList(formencode.FancyValidator): 47 pass
34 """ 48
35 Unique List ! 49 Missing = _Missing()
36 """
37 messages = dict(
38 empty=_('Value cannot be an empty list'),
39 missing_value=_('Value cannot be an empty list'),
40 )
41
42 def _to_python(self, value, state):
43 if isinstance(value, list):
44 return value
45 elif isinstance(value, set):
46 return list(value)
47 elif isinstance(value, tuple):
48 return list(value)
49 elif value is None:
50 return []
51 else:
52 return [value]
53
54 def empty_value(self, value):
55 return []
56 50
57 51
58 class StateObj(object): 52 class StateObj(object):
59 """ 53 """
60 this is needed to translate the messages using _() in validators 54 this is needed to translate the messages using _() in validators
77 state._ = staticmethod(_) 71 state._ = staticmethod(_)
78 #inject validator into state object 72 #inject validator into state object
79 return self.message(key, state, **kwargs) 73 return self.message(key, state, **kwargs)
80 74
81 75
76 def UniqueList():
77 class _UniqueList(formencode.FancyValidator):
78 """
79 Unique List !
80 """
81 messages = dict(
82 empty=_('Value cannot be an empty list'),
83 missing_value=_('Value cannot be an empty list'),
84 )
85
86 def _to_python(self, value, state):
87 def make_unique(value):
88 seen = []
89 return [c for c in value if not (c in seen or seen.append(c))]
90
91 if isinstance(value, list):
92 return make_unique(value)
93 elif isinstance(value, set):
94 return make_unique(list(value))
95 elif isinstance(value, tuple):
96 return make_unique(list(value))
97 elif value is None:
98 return []
99 else:
100 return [value]
101
102 def empty_value(self, value):
103 return []
104
105 return _UniqueList
106
107
108 def UniqueListFromString():
109 class _UniqueListFromString(UniqueList()):
110 def _to_python(self, value, state):
111 if isinstance(value, basestring):
112 value = aslist(value, ',')
113 return super(_UniqueListFromString, self)._to_python(value, state)
114 return _UniqueListFromString
115
116
82 def ValidUsername(edit=False, old_data={}): 117 def ValidUsername(edit=False, old_data={}):
83 class _validator(formencode.validators.FancyValidator): 118 class _validator(formencode.validators.FancyValidator):
84 messages = { 119 messages = {
85 'username_exists': _(u'Username "%(username)s" already exists'), 120 'username_exists': _(u'Username "%(username)s" already exists'),
86 'system_invalid_username': 121 'system_invalid_username':
106 raise formencode.Invalid(msg, value, state) 141 raise formencode.Invalid(msg, value, state)
107 142
108 if re.match(r'^[a-zA-Z0-9\_]{1}[a-zA-Z0-9\-\_\.]*$', value) is None: 143 if re.match(r'^[a-zA-Z0-9\_]{1}[a-zA-Z0-9\-\_\.]*$', value) is None:
109 msg = M(self, 'invalid_username', state) 144 msg = M(self, 'invalid_username', state)
110 raise formencode.Invalid(msg, value, state) 145 raise formencode.Invalid(msg, value, state)
146 return _validator
147
148
149 def ValidRegex(msg=None):
150 class _validator(formencode.validators.Regex):
151 messages = dict(invalid=msg or _('The input is not valid'))
111 return _validator 152 return _validator
112 153
113 154
114 def ValidRepoUser(): 155 def ValidRepoUser():
115 class _validator(formencode.validators.FancyValidator): 156 class _validator(formencode.validators.FancyValidator):
169 ) 210 )
170 211
171 return _validator 212 return _validator
172 213
173 214
174 def ValidReposGroup(edit=False, old_data={}): 215 def ValidRepoGroup(edit=False, old_data={}):
175 class _validator(formencode.validators.FancyValidator): 216 class _validator(formencode.validators.FancyValidator):
176 messages = { 217 messages = {
177 'group_parent_id': _(u'Cannot assign this group as parent'), 218 'group_parent_id': _(u'Cannot assign this group as parent'),
178 'group_exists': _(u'Group "%(group_name)s" already exists'), 219 'group_exists': _(u'Group "%(group_name)s" already exists'),
179 'repo_exists': 220 'repo_exists':
245 msg = M(self, 'invalid_password', state) 286 msg = M(self, 'invalid_password', state)
246 raise formencode.Invalid(msg, value, state,) 287 raise formencode.Invalid(msg, value, state,)
247 return _validator 288 return _validator
248 289
249 290
250 def ValidPasswordsMatch(): 291 def ValidOldPassword(username):
292 class _validator(formencode.validators.FancyValidator):
293 messages = {
294 'invalid_password': _(u'Invalid old password')
295 }
296
297 def validate_python(self, value, state):
298 from rhodecode.lib import auth_modules
299 if not auth_modules.authenticate(username, value, ''):
300 msg = M(self, 'invalid_password', state)
301 raise formencode.Invalid(msg, value, state,
302 error_dict=dict(current_password=msg)
303 )
304 return _validator
305
306
307 def ValidPasswordsMatch(passwd='new_password', passwd_confirmation='password_confirmation'):
251 class _validator(formencode.validators.FancyValidator): 308 class _validator(formencode.validators.FancyValidator):
252 messages = { 309 messages = {
253 'password_mismatch': _(u'Passwords do not match'), 310 'password_mismatch': _(u'Passwords do not match'),
254 } 311 }
255 312
256 def validate_python(self, value, state): 313 def validate_python(self, value, state):
257 314
258 pass_val = value.get('password') or value.get('new_password') 315 pass_val = value.get('password') or value.get(passwd)
259 if pass_val != value['password_confirmation']: 316 if pass_val != value[passwd_confirmation]:
260 msg = M(self, 'password_mismatch', state) 317 msg = M(self, 'password_mismatch', state)
261 raise formencode.Invalid(msg, value, state, 318 raise formencode.Invalid(msg, value, state,
262 error_dict=dict(password_confirmation=msg) 319 error_dict={passwd:msg, passwd_confirmation: msg}
263 ) 320 )
264 return _validator 321 return _validator
265 322
266 323
267 def ValidAuth(): 324 def ValidAuth():
271 'invalid_username': _(u'invalid user name'), 328 'invalid_username': _(u'invalid user name'),
272 'disabled_account': _(u'Your account is disabled') 329 'disabled_account': _(u'Your account is disabled')
273 } 330 }
274 331
275 def validate_python(self, value, state): 332 def validate_python(self, value, state):
276 from rhodecode.lib.auth import authenticate 333 from rhodecode.lib import auth_modules
277 334
278 password = value['password'] 335 password = value['password']
279 username = value['username'] 336 username = value['username']
280 337
281 if not authenticate(username, password): 338 if not auth_modules.authenticate(username, password):
282 user = User.get_by_username(username) 339 user = User.get_by_username(username)
283 if user and not user.active: 340 if user and not user.active:
284 log.warning('user %s is disabled' % username) 341 log.warning('user %s is disabled' % username)
285 msg = M(self, 'disabled_account', state) 342 msg = M(self, 'disabled_account', state)
286 raise formencode.Invalid(msg, value, state, 343 raise formencode.Invalid(msg, value, state,
401 458
402 459
403 def ValidCloneUri(): 460 def ValidCloneUri():
404 from rhodecode.lib.utils import make_ui 461 from rhodecode.lib.utils import make_ui
405 462
406 def url_handler(repo_type, url, ui=None): 463 def url_handler(repo_type, url, ui):
407 if repo_type == 'hg': 464 if repo_type == 'hg':
408 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository 465 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
409 from rhodecode.lib.vcs.utils.hgcompat import httppeer
410 if url.startswith('http'): 466 if url.startswith('http'):
411 ## initially check if it's at least the proper URL 467 # initially check if it's at least the proper URL
412 ## or does it pass basic auth 468 # or does it pass basic auth
413 MercurialRepository._check_url(url) 469 MercurialRepository._check_url(url, ui)
414 httppeer(ui, url)._capabilities()
415 elif url.startswith('svn+http'): 470 elif url.startswith('svn+http'):
416 from hgsubversion.svnrepo import svnremoterepo 471 from hgsubversion.svnrepo import svnremoterepo
417 svnremoterepo(ui, url).capabilities 472 svnremoterepo(ui, url).svn.uuid
418 elif url.startswith('git+http'): 473 elif url.startswith('git+http'):
419 raise NotImplementedError() 474 raise NotImplementedError()
420 else: 475 else:
421 raise Exception('clone from URI %s not allowed' % (url,)) 476 raise Exception('clone from URI %s not allowed' % (url,))
422 477
423 elif repo_type == 'git': 478 elif repo_type == 'git':
424 from rhodecode.lib.vcs.backends.git.repository import GitRepository 479 from rhodecode.lib.vcs.backends.git.repository import GitRepository
425 if url.startswith('http'): 480 if url.startswith('http'):
426 ## initially check if it's at least the proper URL 481 # initially check if it's at least the proper URL
427 ## or does it pass basic auth 482 # or does it pass basic auth
428 GitRepository._check_url(url) 483 GitRepository._check_url(url)
429 elif url.startswith('svn+http'): 484 elif url.startswith('svn+http'):
430 raise NotImplementedError() 485 raise NotImplementedError()
431 elif url.startswith('hg+http'): 486 elif url.startswith('hg+http'):
432 raise NotImplementedError() 487 raise NotImplementedError()
489 return value 544 return value
490 545
491 def validate_python(self, value, state): 546 def validate_python(self, value, state):
492 gr = RepoGroup.get(value) 547 gr = RepoGroup.get(value)
493 gr_name = gr.group_name if gr else None # None means ROOT location 548 gr_name = gr.group_name if gr else None # None means ROOT location
494 val = HasReposGroupPermissionAny('group.write', 'group.admin') 549 # create repositories with write permission on group is set to true
550 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
551 group_admin = HasRepoGroupPermissionAny('group.admin')(gr_name,
552 'can write into group validator')
553 group_write = HasRepoGroupPermissionAny('group.write')(gr_name,
554 'can write into group validator')
555 forbidden = not (group_admin or (group_write and create_on_write))
495 can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository') 556 can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository')
496 forbidden = not val(gr_name, 'can write into group validator') 557 gid = (old_data['repo_group'].get('group_id')
497 value_changed = True # old_data['repo_group'].get('group_id') != safe_int(value) 558 if (old_data and 'repo_group' in old_data) else None)
498 if value_changed: # do check if we changed the value 559 value_changed = gid != safe_int(value)
560 new = not old_data
561 # do check if we changed the value, there's a case that someone got
562 # revoked write permissions to a repository, he still created, we
563 # don't need to check permission if he didn't change the value of
564 # groups in form box
565 if value_changed or new:
499 #parent group need to be existing 566 #parent group need to be existing
500 if gr and forbidden: 567 if gr and forbidden:
501 msg = M(self, 'permission_denied', state) 568 msg = M(self, 'permission_denied', state)
502 raise formencode.Invalid(msg, value, state, 569 raise formencode.Invalid(msg, value, state,
503 error_dict=dict(repo_type=msg) 570 error_dict=dict(repo_type=msg)
532 if can_create_in_root and gr is None: 599 if can_create_in_root and gr is None:
533 #we can create in root, we're fine no validations required 600 #we can create in root, we're fine no validations required
534 return 601 return
535 602
536 forbidden_in_root = gr is None and not can_create_in_root 603 forbidden_in_root = gr is None and not can_create_in_root
537 val = HasReposGroupPermissionAny('group.admin') 604 val = HasRepoGroupPermissionAny('group.admin')
538 forbidden = not val(gr_name, 'can create group validator') 605 forbidden = not val(gr_name, 'can create group validator')
539 if forbidden_in_root or forbidden: 606 if forbidden_in_root or forbidden:
540 msg = M(self, 'permission_denied', state) 607 msg = M(self, 'permission_denied', state)
541 raise formencode.Invalid(msg, value, state, 608 raise formencode.Invalid(msg, value, state,
542 error_dict=dict(group_parent_id=msg) 609 error_dict=dict(group_parent_id=msg)
590 if k.startswith('u_perm_') or k.startswith('g_perm_'): 657 if k.startswith('u_perm_') or k.startswith('g_perm_'):
591 member = k[7:] 658 member = k[7:]
592 t = {'u': 'user', 659 t = {'u': 'user',
593 'g': 'users_group' 660 'g': 'users_group'
594 }[k[0]] 661 }[k[0]]
595 if member == 'default': 662 if member == User.DEFAULT_USER:
596 if str2bool(value.get('repo_private')): 663 if str2bool(value.get('repo_private')):
597 # set none for default when updating to 664 # set none for default when updating to
598 # private repo protects agains form manipulation 665 # private repo protects agains form manipulation
599 v = EMPTY_PERM 666 v = EMPTY_PERM
600 perms_update.add((member, v, t)) 667 perms_update.add((member, v, t))
716 783
717 return _validator 784 return _validator
718 785
719 786
720 def AttrLoginValidator(): 787 def AttrLoginValidator():
721 class _validator(formencode.validators.FancyValidator): 788 class _validator(formencode.validators.UnicodeString):
722 messages = { 789 messages = {
723 'invalid_cn': 790 'invalid_cn':
724 _(u'The LDAP Login attribute of the CN must be specified - ' 791 _(u'The LDAP Login attribute of the CN must be specified - '
725 'this is the name of the attribute that is equivalent ' 792 'this is the name of the attribute that is equivalent '
726 'to "username"') 793 'to "username"')
823 def validate_python(self, value, state): 890 def validate_python(self, value, state):
824 if value != os.path.basename(value): 891 if value != os.path.basename(value):
825 raise formencode.Invalid(self.message('badPath', state), 892 raise formencode.Invalid(self.message('badPath', state),
826 value, state) 893 value, state)
827 return _validator 894 return _validator
895
896
897 def ValidAuthPlugins():
898 class _validator(formencode.validators.FancyValidator):
899 messages = dict(
900 import_duplicate=_('Plugins %(loaded)s and %(next_to_load)s both export the same name')
901 )
902
903 def _to_python(self, value, state):
904 # filter empty values
905 return filter(lambda s: s not in [None, ''], value)
906
907 def validate_python(self, value, state):
908 from rhodecode.lib import auth_modules
909 module_list = value
910 unique_names = {}
911 try:
912 for module in module_list:
913 plugin = auth_modules.loadplugin(module)
914 plugin_name = plugin.name
915 if plugin_name in unique_names:
916 msg = M(self, 'import_duplicate', state,
917 loaded=unique_names[plugin_name],
918 next_to_load=plugin_name)
919 raise formencode.Invalid(msg, value, state)
920 unique_names[plugin_name] = plugin
921 except (ImportError, AttributeError, TypeError), e:
922 raise formencode.Invalid(str(e), value, state)
923
924 return _validator