##// END OF EJS Templates
url-validator: make it more explicit credentials could be a case for repo import validator to fail
super-admin -
r4765:06411f9c default
parent child Browse files
Show More
@@ -1,160 +1,160 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2020 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import re
22 import re
23 import logging
23 import logging
24
24
25
25
26 import ipaddress
26 import ipaddress
27 import colander
27 import colander
28
28
29 from rhodecode.translation import _
29 from rhodecode.translation import _
30 from rhodecode.lib.utils2 import glob2re, safe_unicode
30 from rhodecode.lib.utils2 import glob2re, safe_unicode
31 from rhodecode.lib.ext_json import json
31 from rhodecode.lib.ext_json import json
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 def ip_addr_validator(node, value):
36 def ip_addr_validator(node, value):
37 try:
37 try:
38 # this raises an ValueError if address is not IpV4 or IpV6
38 # this raises an ValueError if address is not IpV4 or IpV6
39 ipaddress.ip_network(safe_unicode(value), strict=False)
39 ipaddress.ip_network(safe_unicode(value), strict=False)
40 except ValueError:
40 except ValueError:
41 msg = _(u'Please enter a valid IPv4 or IpV6 address')
41 msg = _(u'Please enter a valid IPv4 or IpV6 address')
42 raise colander.Invalid(node, msg)
42 raise colander.Invalid(node, msg)
43
43
44
44
45 class IpAddrValidator(object):
45 class IpAddrValidator(object):
46 def __init__(self, strict=True):
46 def __init__(self, strict=True):
47 self.strict = strict
47 self.strict = strict
48
48
49 def __call__(self, node, value):
49 def __call__(self, node, value):
50 try:
50 try:
51 # this raises an ValueError if address is not IpV4 or IpV6
51 # this raises an ValueError if address is not IpV4 or IpV6
52 ipaddress.ip_network(safe_unicode(value), strict=self.strict)
52 ipaddress.ip_network(safe_unicode(value), strict=self.strict)
53 except ValueError:
53 except ValueError:
54 msg = _(u'Please enter a valid IPv4 or IpV6 address')
54 msg = _(u'Please enter a valid IPv4 or IpV6 address')
55 raise colander.Invalid(node, msg)
55 raise colander.Invalid(node, msg)
56
56
57
57
58 def glob_validator(node, value):
58 def glob_validator(node, value):
59 try:
59 try:
60 re.compile('^' + glob2re(value) + '$')
60 re.compile('^' + glob2re(value) + '$')
61 except Exception:
61 except Exception:
62 msg = _(u'Invalid glob pattern')
62 msg = _(u'Invalid glob pattern')
63 raise colander.Invalid(node, msg)
63 raise colander.Invalid(node, msg)
64
64
65
65
66 def valid_name_validator(node, value):
66 def valid_name_validator(node, value):
67 from rhodecode.model.validation_schema import types
67 from rhodecode.model.validation_schema import types
68 if value is types.RootLocation:
68 if value is types.RootLocation:
69 return
69 return
70
70
71 msg = _('Name must start with a letter or number. Got `{}`').format(value)
71 msg = _('Name must start with a letter or number. Got `{}`').format(value)
72 if not re.match(r'^[a-zA-z0-9]{1,}', value):
72 if not re.match(r'^[a-zA-z0-9]{1,}', value):
73 raise colander.Invalid(node, msg)
73 raise colander.Invalid(node, msg)
74
74
75
75
76 class InvalidCloneUrl(Exception):
76 class InvalidCloneUrl(Exception):
77 allowed_prefixes = ()
77 allowed_prefixes = ()
78
78
79
79
80 def url_validator(url, repo_type, config):
80 def url_validator(url, repo_type, config):
81 from rhodecode.lib.vcs.backends.hg import MercurialRepository
81 from rhodecode.lib.vcs.backends.hg import MercurialRepository
82 from rhodecode.lib.vcs.backends.git import GitRepository
82 from rhodecode.lib.vcs.backends.git import GitRepository
83 from rhodecode.lib.vcs.backends.svn import SubversionRepository
83 from rhodecode.lib.vcs.backends.svn import SubversionRepository
84
84
85 if repo_type == 'hg':
85 if repo_type == 'hg':
86 allowed_prefixes = ('http', 'svn+http', 'git+http')
86 allowed_prefixes = ('http', 'svn+http', 'git+http')
87
87
88 if 'http' in url[:4]:
88 if 'http' in url[:4]:
89 # initially check if it's at least the proper URL
89 # initially check if it's at least the proper URL
90 # or does it pass basic auth
90 # or does it pass basic auth
91
91
92 return MercurialRepository.check_url(url, config)
92 return MercurialRepository.check_url(url, config)
93 elif 'svn+http' in url[:8]: # svn->hg import
93 elif 'svn+http' in url[:8]: # svn->hg import
94 SubversionRepository.check_url(url, config)
94 SubversionRepository.check_url(url, config)
95 elif 'git+http' in url[:8]: # git->hg import
95 elif 'git+http' in url[:8]: # git->hg import
96 raise NotImplementedError()
96 raise NotImplementedError()
97 else:
97 else:
98 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
98 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
99 'Allowed url must start with one of %s'
99 'Allowed url must start with one of %s'
100 % (url, ','.join(allowed_prefixes)))
100 % (url, ','.join(allowed_prefixes)))
101 exc.allowed_prefixes = allowed_prefixes
101 exc.allowed_prefixes = allowed_prefixes
102 raise exc
102 raise exc
103
103
104 elif repo_type == 'git':
104 elif repo_type == 'git':
105 allowed_prefixes = ('http', 'svn+http', 'hg+http')
105 allowed_prefixes = ('http', 'svn+http', 'hg+http')
106 if 'http' in url[:4]:
106 if 'http' in url[:4]:
107 # initially check if it's at least the proper URL
107 # initially check if it's at least the proper URL
108 # or does it pass basic auth
108 # or does it pass basic auth
109 return GitRepository.check_url(url, config)
109 return GitRepository.check_url(url, config)
110 elif 'svn+http' in url[:8]: # svn->git import
110 elif 'svn+http' in url[:8]: # svn->git import
111 raise NotImplementedError()
111 raise NotImplementedError()
112 elif 'hg+http' in url[:8]: # hg->git import
112 elif 'hg+http' in url[:8]: # hg->git import
113 raise NotImplementedError()
113 raise NotImplementedError()
114 else:
114 else:
115 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
115 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
116 'Allowed url must start with one of %s'
116 'Allowed url must start with one of %s'
117 % (url, ','.join(allowed_prefixes)))
117 % (url, ','.join(allowed_prefixes)))
118 exc.allowed_prefixes = allowed_prefixes
118 exc.allowed_prefixes = allowed_prefixes
119 raise exc
119 raise exc
120 elif repo_type == 'svn':
120 elif repo_type == 'svn':
121 # no validation for SVN yet
121 # no validation for SVN yet
122 return
122 return
123
123
124 raise InvalidCloneUrl('Invalid repo type specified: `{}`'.format(repo_type))
124 raise InvalidCloneUrl('Invalid repo type specified: `{}`'.format(repo_type))
125
125
126
126
127 class CloneUriValidator(object):
127 class CloneUriValidator(object):
128 def __init__(self, repo_type):
128 def __init__(self, repo_type):
129 self.repo_type = repo_type
129 self.repo_type = repo_type
130
130
131 def __call__(self, node, value):
131 def __call__(self, node, value):
132
132
133 from rhodecode.lib.utils import make_db_config
133 from rhodecode.lib.utils import make_db_config
134 try:
134 try:
135 config = make_db_config(clear_session=False)
135 config = make_db_config(clear_session=False)
136 url_validator(value, self.repo_type, config)
136 url_validator(value, self.repo_type, config)
137 except InvalidCloneUrl as e:
137 except InvalidCloneUrl as e:
138 log.warning(e)
138 log.warning(e)
139 raise colander.Invalid(node, e.message)
139 raise colander.Invalid(node, e.message)
140 except Exception:
140 except Exception:
141 log.exception('Url validation failed')
141 log.exception('Url validation failed')
142 msg = _(u'invalid clone url for {repo_type} repository').format(
142 msg = _(u'invalid clone url or credentials for {repo_type} repository').format(
143 repo_type=self.repo_type)
143 repo_type=self.repo_type)
144 raise colander.Invalid(node, msg)
144 raise colander.Invalid(node, msg)
145
145
146
146
147 def json_validator(node, value):
147 def json_validator(node, value):
148 try:
148 try:
149 json.loads(value)
149 json.loads(value)
150 except (Exception,) as e:
150 except (Exception,) as e:
151 msg = _(u'Please enter a valid json object')
151 msg = _(u'Please enter a valid json object')
152 raise colander.Invalid(node, msg)
152 raise colander.Invalid(node, msg)
153
153
154
154
155 def json_validator_with_exc(node, value):
155 def json_validator_with_exc(node, value):
156 try:
156 try:
157 json.loads(value)
157 json.loads(value)
158 except (Exception,) as e:
158 except (Exception,) as e:
159 msg = _(u'Please enter a valid json object: `{}`'.format(e))
159 msg = _(u'Please enter a valid json object: `{}`'.format(e))
160 raise colander.Invalid(node, msg)
160 raise colander.Invalid(node, msg)
@@ -1,1116 +1,1116 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Set of generic validators
22 Set of generic validators
23 """
23 """
24
24
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import collections
29 import collections
30
30
31 import formencode
31 import formencode
32 import ipaddress
32 import ipaddress
33 from formencode.validators import (
33 from formencode.validators import (
34 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
34 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
35 NotEmpty, IPAddress, CIDR, String, FancyValidator
35 NotEmpty, IPAddress, CIDR, String, FancyValidator
36 )
36 )
37
37
38 from sqlalchemy.sql.expression import true
38 from sqlalchemy.sql.expression import true
39 from sqlalchemy.util import OrderedSet
39 from sqlalchemy.util import OrderedSet
40 from pyramid import compat
40 from pyramid import compat
41
41
42 from rhodecode.authentication import (
42 from rhodecode.authentication import (
43 legacy_plugin_prefix, _import_legacy_plugin)
43 legacy_plugin_prefix, _import_legacy_plugin)
44 from rhodecode.authentication.base import loadplugin
44 from rhodecode.authentication.base import loadplugin
45 from rhodecode.apps._base import ADMIN_PREFIX
45 from rhodecode.apps._base import ADMIN_PREFIX
46 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
46 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
47 from rhodecode.lib.utils import repo_name_slug, make_db_config
47 from rhodecode.lib.utils import repo_name_slug, make_db_config
48 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5, safe_unicode
48 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5, safe_unicode
49 from rhodecode.lib.vcs.backends.git.repository import GitRepository
49 from rhodecode.lib.vcs.backends.git.repository import GitRepository
50 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
50 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
51 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
51 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
52 from rhodecode.model.db import (
52 from rhodecode.model.db import (
53 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
53 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
54 from rhodecode.model.settings import VcsSettingsModel
54 from rhodecode.model.settings import VcsSettingsModel
55
55
56 # silence warnings and pylint
56 # silence warnings and pylint
57 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
57 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
58 NotEmpty, IPAddress, CIDR, String, FancyValidator
58 NotEmpty, IPAddress, CIDR, String, FancyValidator
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class _Missing(object):
63 class _Missing(object):
64 pass
64 pass
65
65
66
66
67 Missing = _Missing()
67 Missing = _Missing()
68
68
69
69
70 def M(self, key, state, **kwargs):
70 def M(self, key, state, **kwargs):
71 """
71 """
72 returns string from self.message based on given key,
72 returns string from self.message based on given key,
73 passed kw params are used to substitute %(named)s params inside
73 passed kw params are used to substitute %(named)s params inside
74 translated strings
74 translated strings
75
75
76 :param msg:
76 :param msg:
77 :param state:
77 :param state:
78 """
78 """
79
79
80 #state._ = staticmethod(_)
80 #state._ = staticmethod(_)
81 # inject validator into state object
81 # inject validator into state object
82 return self.message(key, state, **kwargs)
82 return self.message(key, state, **kwargs)
83
83
84
84
85 def UniqueList(localizer, convert=None):
85 def UniqueList(localizer, convert=None):
86 _ = localizer
86 _ = localizer
87
87
88 class _validator(formencode.FancyValidator):
88 class _validator(formencode.FancyValidator):
89 """
89 """
90 Unique List !
90 Unique List !
91 """
91 """
92 messages = {
92 messages = {
93 'empty': _(u'Value cannot be an empty list'),
93 'empty': _(u'Value cannot be an empty list'),
94 'missing_value': _(u'Value cannot be an empty list'),
94 'missing_value': _(u'Value cannot be an empty list'),
95 }
95 }
96
96
97 def _to_python(self, value, state):
97 def _to_python(self, value, state):
98 ret_val = []
98 ret_val = []
99
99
100 def make_unique(value):
100 def make_unique(value):
101 seen = []
101 seen = []
102 return [c for c in value if not (c in seen or seen.append(c))]
102 return [c for c in value if not (c in seen or seen.append(c))]
103
103
104 if isinstance(value, list):
104 if isinstance(value, list):
105 ret_val = make_unique(value)
105 ret_val = make_unique(value)
106 elif isinstance(value, set):
106 elif isinstance(value, set):
107 ret_val = make_unique(list(value))
107 ret_val = make_unique(list(value))
108 elif isinstance(value, tuple):
108 elif isinstance(value, tuple):
109 ret_val = make_unique(list(value))
109 ret_val = make_unique(list(value))
110 elif value is None:
110 elif value is None:
111 ret_val = []
111 ret_val = []
112 else:
112 else:
113 ret_val = [value]
113 ret_val = [value]
114
114
115 if convert:
115 if convert:
116 ret_val = map(convert, ret_val)
116 ret_val = map(convert, ret_val)
117 return ret_val
117 return ret_val
118
118
119 def empty_value(self, value):
119 def empty_value(self, value):
120 return []
120 return []
121 return _validator
121 return _validator
122
122
123
123
124 def UniqueListFromString(localizer):
124 def UniqueListFromString(localizer):
125 _ = localizer
125 _ = localizer
126
126
127 class _validator(UniqueList(localizer)):
127 class _validator(UniqueList(localizer)):
128 def _to_python(self, value, state):
128 def _to_python(self, value, state):
129 if isinstance(value, compat.string_types):
129 if isinstance(value, compat.string_types):
130 value = aslist(value, ',')
130 value = aslist(value, ',')
131 return super(_validator, self)._to_python(value, state)
131 return super(_validator, self)._to_python(value, state)
132 return _validator
132 return _validator
133
133
134
134
135 def ValidSvnPattern(localizer, section, repo_name=None):
135 def ValidSvnPattern(localizer, section, repo_name=None):
136 _ = localizer
136 _ = localizer
137
137
138 class _validator(formencode.validators.FancyValidator):
138 class _validator(formencode.validators.FancyValidator):
139 messages = {
139 messages = {
140 'pattern_exists': _(u'Pattern already exists'),
140 'pattern_exists': _(u'Pattern already exists'),
141 }
141 }
142
142
143 def validate_python(self, value, state):
143 def validate_python(self, value, state):
144 if not value:
144 if not value:
145 return
145 return
146 model = VcsSettingsModel(repo=repo_name)
146 model = VcsSettingsModel(repo=repo_name)
147 ui_settings = model.get_svn_patterns(section=section)
147 ui_settings = model.get_svn_patterns(section=section)
148 for entry in ui_settings:
148 for entry in ui_settings:
149 if value == entry.value:
149 if value == entry.value:
150 msg = M(self, 'pattern_exists', state)
150 msg = M(self, 'pattern_exists', state)
151 raise formencode.Invalid(msg, value, state)
151 raise formencode.Invalid(msg, value, state)
152 return _validator
152 return _validator
153
153
154
154
155 def ValidUsername(localizer, edit=False, old_data=None):
155 def ValidUsername(localizer, edit=False, old_data=None):
156 _ = localizer
156 _ = localizer
157 old_data = old_data or {}
157 old_data = old_data or {}
158
158
159 class _validator(formencode.validators.FancyValidator):
159 class _validator(formencode.validators.FancyValidator):
160 messages = {
160 messages = {
161 'username_exists': _(u'Username "%(username)s" already exists'),
161 'username_exists': _(u'Username "%(username)s" already exists'),
162 'system_invalid_username':
162 'system_invalid_username':
163 _(u'Username "%(username)s" is forbidden'),
163 _(u'Username "%(username)s" is forbidden'),
164 'invalid_username':
164 'invalid_username':
165 _(u'Username may only contain alphanumeric characters '
165 _(u'Username may only contain alphanumeric characters '
166 u'underscores, periods or dashes and must begin with '
166 u'underscores, periods or dashes and must begin with '
167 u'alphanumeric character or underscore')
167 u'alphanumeric character or underscore')
168 }
168 }
169
169
170 def validate_python(self, value, state):
170 def validate_python(self, value, state):
171 if value in ['default', 'new_user']:
171 if value in ['default', 'new_user']:
172 msg = M(self, 'system_invalid_username', state, username=value)
172 msg = M(self, 'system_invalid_username', state, username=value)
173 raise formencode.Invalid(msg, value, state)
173 raise formencode.Invalid(msg, value, state)
174 # check if user is unique
174 # check if user is unique
175 old_un = None
175 old_un = None
176 if edit:
176 if edit:
177 old_un = User.get(old_data.get('user_id')).username
177 old_un = User.get(old_data.get('user_id')).username
178
178
179 if old_un != value or not edit:
179 if old_un != value or not edit:
180 if User.get_by_username(value, case_insensitive=True):
180 if User.get_by_username(value, case_insensitive=True):
181 msg = M(self, 'username_exists', state, username=value)
181 msg = M(self, 'username_exists', state, username=value)
182 raise formencode.Invalid(msg, value, state)
182 raise formencode.Invalid(msg, value, state)
183
183
184 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
184 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
185 is None):
185 is None):
186 msg = M(self, 'invalid_username', state)
186 msg = M(self, 'invalid_username', state)
187 raise formencode.Invalid(msg, value, state)
187 raise formencode.Invalid(msg, value, state)
188 return _validator
188 return _validator
189
189
190
190
191 def ValidRepoUser(localizer, allow_disabled=False):
191 def ValidRepoUser(localizer, allow_disabled=False):
192 _ = localizer
192 _ = localizer
193
193
194 class _validator(formencode.validators.FancyValidator):
194 class _validator(formencode.validators.FancyValidator):
195 messages = {
195 messages = {
196 'invalid_username': _(u'Username %(username)s is not valid'),
196 'invalid_username': _(u'Username %(username)s is not valid'),
197 'disabled_username': _(u'Username %(username)s is disabled')
197 'disabled_username': _(u'Username %(username)s is disabled')
198 }
198 }
199
199
200 def validate_python(self, value, state):
200 def validate_python(self, value, state):
201 try:
201 try:
202 user = User.query().filter(User.username == value).one()
202 user = User.query().filter(User.username == value).one()
203 except Exception:
203 except Exception:
204 msg = M(self, 'invalid_username', state, username=value)
204 msg = M(self, 'invalid_username', state, username=value)
205 raise formencode.Invalid(
205 raise formencode.Invalid(
206 msg, value, state, error_dict={'username': msg}
206 msg, value, state, error_dict={'username': msg}
207 )
207 )
208 if user and (not allow_disabled and not user.active):
208 if user and (not allow_disabled and not user.active):
209 msg = M(self, 'disabled_username', state, username=value)
209 msg = M(self, 'disabled_username', state, username=value)
210 raise formencode.Invalid(
210 raise formencode.Invalid(
211 msg, value, state, error_dict={'username': msg}
211 msg, value, state, error_dict={'username': msg}
212 )
212 )
213 return _validator
213 return _validator
214
214
215
215
216 def ValidUserGroup(localizer, edit=False, old_data=None):
216 def ValidUserGroup(localizer, edit=False, old_data=None):
217 _ = localizer
217 _ = localizer
218 old_data = old_data or {}
218 old_data = old_data or {}
219
219
220 class _validator(formencode.validators.FancyValidator):
220 class _validator(formencode.validators.FancyValidator):
221 messages = {
221 messages = {
222 'invalid_group': _(u'Invalid user group name'),
222 'invalid_group': _(u'Invalid user group name'),
223 'group_exist': _(u'User group `%(usergroup)s` already exists'),
223 'group_exist': _(u'User group `%(usergroup)s` already exists'),
224 'invalid_usergroup_name':
224 'invalid_usergroup_name':
225 _(u'user group name may only contain alphanumeric '
225 _(u'user group name may only contain alphanumeric '
226 u'characters underscores, periods or dashes and must begin '
226 u'characters underscores, periods or dashes and must begin '
227 u'with alphanumeric character')
227 u'with alphanumeric character')
228 }
228 }
229
229
230 def validate_python(self, value, state):
230 def validate_python(self, value, state):
231 if value in ['default']:
231 if value in ['default']:
232 msg = M(self, 'invalid_group', state)
232 msg = M(self, 'invalid_group', state)
233 raise formencode.Invalid(
233 raise formencode.Invalid(
234 msg, value, state, error_dict={'users_group_name': msg}
234 msg, value, state, error_dict={'users_group_name': msg}
235 )
235 )
236 # check if group is unique
236 # check if group is unique
237 old_ugname = None
237 old_ugname = None
238 if edit:
238 if edit:
239 old_id = old_data.get('users_group_id')
239 old_id = old_data.get('users_group_id')
240 old_ugname = UserGroup.get(old_id).users_group_name
240 old_ugname = UserGroup.get(old_id).users_group_name
241
241
242 if old_ugname != value or not edit:
242 if old_ugname != value or not edit:
243 is_existing_group = UserGroup.get_by_group_name(
243 is_existing_group = UserGroup.get_by_group_name(
244 value, case_insensitive=True)
244 value, case_insensitive=True)
245 if is_existing_group:
245 if is_existing_group:
246 msg = M(self, 'group_exist', state, usergroup=value)
246 msg = M(self, 'group_exist', state, usergroup=value)
247 raise formencode.Invalid(
247 raise formencode.Invalid(
248 msg, value, state, error_dict={'users_group_name': msg}
248 msg, value, state, error_dict={'users_group_name': msg}
249 )
249 )
250
250
251 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
251 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
252 msg = M(self, 'invalid_usergroup_name', state)
252 msg = M(self, 'invalid_usergroup_name', state)
253 raise formencode.Invalid(
253 raise formencode.Invalid(
254 msg, value, state, error_dict={'users_group_name': msg}
254 msg, value, state, error_dict={'users_group_name': msg}
255 )
255 )
256 return _validator
256 return _validator
257
257
258
258
259 def ValidRepoGroup(localizer, edit=False, old_data=None, can_create_in_root=False):
259 def ValidRepoGroup(localizer, edit=False, old_data=None, can_create_in_root=False):
260 _ = localizer
260 _ = localizer
261 old_data = old_data or {}
261 old_data = old_data or {}
262
262
263 class _validator(formencode.validators.FancyValidator):
263 class _validator(formencode.validators.FancyValidator):
264 messages = {
264 messages = {
265 'group_parent_id': _(u'Cannot assign this group as parent'),
265 'group_parent_id': _(u'Cannot assign this group as parent'),
266 'group_exists': _(u'Group "%(group_name)s" already exists'),
266 'group_exists': _(u'Group "%(group_name)s" already exists'),
267 'repo_exists': _(u'Repository with name "%(group_name)s" '
267 'repo_exists': _(u'Repository with name "%(group_name)s" '
268 u'already exists'),
268 u'already exists'),
269 'permission_denied': _(u"no permission to store repository group"
269 'permission_denied': _(u"no permission to store repository group"
270 u"in this location"),
270 u"in this location"),
271 'permission_denied_root': _(
271 'permission_denied_root': _(
272 u"no permission to store repository group "
272 u"no permission to store repository group "
273 u"in root location")
273 u"in root location")
274 }
274 }
275
275
276 def _to_python(self, value, state):
276 def _to_python(self, value, state):
277 group_name = repo_name_slug(value.get('group_name', ''))
277 group_name = repo_name_slug(value.get('group_name', ''))
278 group_parent_id = safe_int(value.get('group_parent_id'))
278 group_parent_id = safe_int(value.get('group_parent_id'))
279 gr = RepoGroup.get(group_parent_id)
279 gr = RepoGroup.get(group_parent_id)
280 if gr:
280 if gr:
281 parent_group_path = gr.full_path
281 parent_group_path = gr.full_path
282 # value needs to be aware of group name in order to check
282 # value needs to be aware of group name in order to check
283 # db key This is an actual just the name to store in the
283 # db key This is an actual just the name to store in the
284 # database
284 # database
285 group_name_full = (
285 group_name_full = (
286 parent_group_path + RepoGroup.url_sep() + group_name)
286 parent_group_path + RepoGroup.url_sep() + group_name)
287 else:
287 else:
288 group_name_full = group_name
288 group_name_full = group_name
289
289
290 value['group_name'] = group_name
290 value['group_name'] = group_name
291 value['group_name_full'] = group_name_full
291 value['group_name_full'] = group_name_full
292 value['group_parent_id'] = group_parent_id
292 value['group_parent_id'] = group_parent_id
293 return value
293 return value
294
294
295 def validate_python(self, value, state):
295 def validate_python(self, value, state):
296
296
297 old_group_name = None
297 old_group_name = None
298 group_name = value.get('group_name')
298 group_name = value.get('group_name')
299 group_name_full = value.get('group_name_full')
299 group_name_full = value.get('group_name_full')
300 group_parent_id = safe_int(value.get('group_parent_id'))
300 group_parent_id = safe_int(value.get('group_parent_id'))
301 if group_parent_id == -1:
301 if group_parent_id == -1:
302 group_parent_id = None
302 group_parent_id = None
303
303
304 group_obj = RepoGroup.get(old_data.get('group_id'))
304 group_obj = RepoGroup.get(old_data.get('group_id'))
305 parent_group_changed = False
305 parent_group_changed = False
306 if edit:
306 if edit:
307 old_group_name = group_obj.group_name
307 old_group_name = group_obj.group_name
308 old_group_parent_id = group_obj.group_parent_id
308 old_group_parent_id = group_obj.group_parent_id
309
309
310 if group_parent_id != old_group_parent_id:
310 if group_parent_id != old_group_parent_id:
311 parent_group_changed = True
311 parent_group_changed = True
312
312
313 # TODO: mikhail: the following if statement is not reached
313 # TODO: mikhail: the following if statement is not reached
314 # since group_parent_id's OneOf validation fails before.
314 # since group_parent_id's OneOf validation fails before.
315 # Can be removed.
315 # Can be removed.
316
316
317 # check against setting a parent of self
317 # check against setting a parent of self
318 parent_of_self = (
318 parent_of_self = (
319 old_data['group_id'] == group_parent_id
319 old_data['group_id'] == group_parent_id
320 if group_parent_id else False
320 if group_parent_id else False
321 )
321 )
322 if parent_of_self:
322 if parent_of_self:
323 msg = M(self, 'group_parent_id', state)
323 msg = M(self, 'group_parent_id', state)
324 raise formencode.Invalid(
324 raise formencode.Invalid(
325 msg, value, state, error_dict={'group_parent_id': msg}
325 msg, value, state, error_dict={'group_parent_id': msg}
326 )
326 )
327
327
328 # group we're moving current group inside
328 # group we're moving current group inside
329 child_group = None
329 child_group = None
330 if group_parent_id:
330 if group_parent_id:
331 child_group = RepoGroup.query().filter(
331 child_group = RepoGroup.query().filter(
332 RepoGroup.group_id == group_parent_id).scalar()
332 RepoGroup.group_id == group_parent_id).scalar()
333
333
334 # do a special check that we cannot move a group to one of
334 # do a special check that we cannot move a group to one of
335 # it's children
335 # it's children
336 if edit and child_group:
336 if edit and child_group:
337 parents = [x.group_id for x in child_group.parents]
337 parents = [x.group_id for x in child_group.parents]
338 move_to_children = old_data['group_id'] in parents
338 move_to_children = old_data['group_id'] in parents
339 if move_to_children:
339 if move_to_children:
340 msg = M(self, 'group_parent_id', state)
340 msg = M(self, 'group_parent_id', state)
341 raise formencode.Invalid(
341 raise formencode.Invalid(
342 msg, value, state, error_dict={'group_parent_id': msg})
342 msg, value, state, error_dict={'group_parent_id': msg})
343
343
344 # Check if we have permission to store in the parent.
344 # Check if we have permission to store in the parent.
345 # Only check if the parent group changed.
345 # Only check if the parent group changed.
346 if parent_group_changed:
346 if parent_group_changed:
347 if child_group is None:
347 if child_group is None:
348 if not can_create_in_root:
348 if not can_create_in_root:
349 msg = M(self, 'permission_denied_root', state)
349 msg = M(self, 'permission_denied_root', state)
350 raise formencode.Invalid(
350 raise formencode.Invalid(
351 msg, value, state,
351 msg, value, state,
352 error_dict={'group_parent_id': msg})
352 error_dict={'group_parent_id': msg})
353 else:
353 else:
354 valid = HasRepoGroupPermissionAny('group.admin')
354 valid = HasRepoGroupPermissionAny('group.admin')
355 forbidden = not valid(
355 forbidden = not valid(
356 child_group.group_name, 'can create group validator')
356 child_group.group_name, 'can create group validator')
357 if forbidden:
357 if forbidden:
358 msg = M(self, 'permission_denied', state)
358 msg = M(self, 'permission_denied', state)
359 raise formencode.Invalid(
359 raise formencode.Invalid(
360 msg, value, state,
360 msg, value, state,
361 error_dict={'group_parent_id': msg})
361 error_dict={'group_parent_id': msg})
362
362
363 # if we change the name or it's new group, check for existing names
363 # if we change the name or it's new group, check for existing names
364 # or repositories with the same name
364 # or repositories with the same name
365 if old_group_name != group_name_full or not edit:
365 if old_group_name != group_name_full or not edit:
366 # check group
366 # check group
367 gr = RepoGroup.get_by_group_name(group_name_full)
367 gr = RepoGroup.get_by_group_name(group_name_full)
368 if gr:
368 if gr:
369 msg = M(self, 'group_exists', state, group_name=group_name)
369 msg = M(self, 'group_exists', state, group_name=group_name)
370 raise formencode.Invalid(
370 raise formencode.Invalid(
371 msg, value, state, error_dict={'group_name': msg})
371 msg, value, state, error_dict={'group_name': msg})
372
372
373 # check for same repo
373 # check for same repo
374 repo = Repository.get_by_repo_name(group_name_full)
374 repo = Repository.get_by_repo_name(group_name_full)
375 if repo:
375 if repo:
376 msg = M(self, 'repo_exists', state, group_name=group_name)
376 msg = M(self, 'repo_exists', state, group_name=group_name)
377 raise formencode.Invalid(
377 raise formencode.Invalid(
378 msg, value, state, error_dict={'group_name': msg})
378 msg, value, state, error_dict={'group_name': msg})
379 return _validator
379 return _validator
380
380
381
381
382 def ValidPassword(localizer):
382 def ValidPassword(localizer):
383 _ = localizer
383 _ = localizer
384
384
385 class _validator(formencode.validators.FancyValidator):
385 class _validator(formencode.validators.FancyValidator):
386 messages = {
386 messages = {
387 'invalid_password':
387 'invalid_password':
388 _(u'Invalid characters (non-ascii) in password')
388 _(u'Invalid characters (non-ascii) in password')
389 }
389 }
390
390
391 def validate_python(self, value, state):
391 def validate_python(self, value, state):
392 try:
392 try:
393 (value or '').decode('ascii')
393 (value or '').decode('ascii')
394 except UnicodeError:
394 except UnicodeError:
395 msg = M(self, 'invalid_password', state)
395 msg = M(self, 'invalid_password', state)
396 raise formencode.Invalid(msg, value, state,)
396 raise formencode.Invalid(msg, value, state,)
397 return _validator
397 return _validator
398
398
399
399
400 def ValidPasswordsMatch(
400 def ValidPasswordsMatch(
401 localizer, passwd='new_password',
401 localizer, passwd='new_password',
402 passwd_confirmation='password_confirmation'):
402 passwd_confirmation='password_confirmation'):
403 _ = localizer
403 _ = localizer
404
404
405 class _validator(formencode.validators.FancyValidator):
405 class _validator(formencode.validators.FancyValidator):
406 messages = {
406 messages = {
407 'password_mismatch': _(u'Passwords do not match'),
407 'password_mismatch': _(u'Passwords do not match'),
408 }
408 }
409
409
410 def validate_python(self, value, state):
410 def validate_python(self, value, state):
411
411
412 pass_val = value.get('password') or value.get(passwd)
412 pass_val = value.get('password') or value.get(passwd)
413 if pass_val != value[passwd_confirmation]:
413 if pass_val != value[passwd_confirmation]:
414 msg = M(self, 'password_mismatch', state)
414 msg = M(self, 'password_mismatch', state)
415 raise formencode.Invalid(
415 raise formencode.Invalid(
416 msg, value, state,
416 msg, value, state,
417 error_dict={passwd: msg, passwd_confirmation: msg}
417 error_dict={passwd: msg, passwd_confirmation: msg}
418 )
418 )
419 return _validator
419 return _validator
420
420
421
421
422 def ValidAuth(localizer):
422 def ValidAuth(localizer):
423 _ = localizer
423 _ = localizer
424
424
425 class _validator(formencode.validators.FancyValidator):
425 class _validator(formencode.validators.FancyValidator):
426 messages = {
426 messages = {
427 'invalid_password': _(u'invalid password'),
427 'invalid_password': _(u'invalid password'),
428 'invalid_username': _(u'invalid user name'),
428 'invalid_username': _(u'invalid user name'),
429 'disabled_account': _(u'Your account is disabled')
429 'disabled_account': _(u'Your account is disabled')
430 }
430 }
431
431
432 def validate_python(self, value, state):
432 def validate_python(self, value, state):
433 from rhodecode.authentication.base import authenticate, HTTP_TYPE
433 from rhodecode.authentication.base import authenticate, HTTP_TYPE
434
434
435 password = value['password']
435 password = value['password']
436 username = value['username']
436 username = value['username']
437
437
438 if not authenticate(username, password, '', HTTP_TYPE,
438 if not authenticate(username, password, '', HTTP_TYPE,
439 skip_missing=True):
439 skip_missing=True):
440 user = User.get_by_username(username)
440 user = User.get_by_username(username)
441 if user and not user.active:
441 if user and not user.active:
442 log.warning('user %s is disabled', username)
442 log.warning('user %s is disabled', username)
443 msg = M(self, 'disabled_account', state)
443 msg = M(self, 'disabled_account', state)
444 raise formencode.Invalid(
444 raise formencode.Invalid(
445 msg, value, state, error_dict={'username': msg}
445 msg, value, state, error_dict={'username': msg}
446 )
446 )
447 else:
447 else:
448 log.warning('user `%s` failed to authenticate', username)
448 log.warning('user `%s` failed to authenticate', username)
449 msg = M(self, 'invalid_username', state)
449 msg = M(self, 'invalid_username', state)
450 msg2 = M(self, 'invalid_password', state)
450 msg2 = M(self, 'invalid_password', state)
451 raise formencode.Invalid(
451 raise formencode.Invalid(
452 msg, value, state,
452 msg, value, state,
453 error_dict={'username': msg, 'password': msg2}
453 error_dict={'username': msg, 'password': msg2}
454 )
454 )
455 return _validator
455 return _validator
456
456
457
457
458 def ValidRepoName(localizer, edit=False, old_data=None):
458 def ValidRepoName(localizer, edit=False, old_data=None):
459 old_data = old_data or {}
459 old_data = old_data or {}
460 _ = localizer
460 _ = localizer
461
461
462 class _validator(formencode.validators.FancyValidator):
462 class _validator(formencode.validators.FancyValidator):
463 messages = {
463 messages = {
464 'invalid_repo_name':
464 'invalid_repo_name':
465 _(u'Repository name %(repo)s is disallowed'),
465 _(u'Repository name %(repo)s is disallowed'),
466 # top level
466 # top level
467 'repository_exists': _(u'Repository with name %(repo)s '
467 'repository_exists': _(u'Repository with name %(repo)s '
468 u'already exists'),
468 u'already exists'),
469 'group_exists': _(u'Repository group with name "%(repo)s" '
469 'group_exists': _(u'Repository group with name "%(repo)s" '
470 u'already exists'),
470 u'already exists'),
471 # inside a group
471 # inside a group
472 'repository_in_group_exists': _(u'Repository with name %(repo)s '
472 'repository_in_group_exists': _(u'Repository with name %(repo)s '
473 u'exists in group "%(group)s"'),
473 u'exists in group "%(group)s"'),
474 'group_in_group_exists': _(
474 'group_in_group_exists': _(
475 u'Repository group with name "%(repo)s" '
475 u'Repository group with name "%(repo)s" '
476 u'exists in group "%(group)s"'),
476 u'exists in group "%(group)s"'),
477 }
477 }
478
478
479 def _to_python(self, value, state):
479 def _to_python(self, value, state):
480 repo_name = repo_name_slug(value.get('repo_name', ''))
480 repo_name = repo_name_slug(value.get('repo_name', ''))
481 repo_group = value.get('repo_group')
481 repo_group = value.get('repo_group')
482 if repo_group:
482 if repo_group:
483 gr = RepoGroup.get(repo_group)
483 gr = RepoGroup.get(repo_group)
484 group_path = gr.full_path
484 group_path = gr.full_path
485 group_name = gr.group_name
485 group_name = gr.group_name
486 # value needs to be aware of group name in order to check
486 # value needs to be aware of group name in order to check
487 # db key This is an actual just the name to store in the
487 # db key This is an actual just the name to store in the
488 # database
488 # database
489 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
489 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
490 else:
490 else:
491 group_name = group_path = ''
491 group_name = group_path = ''
492 repo_name_full = repo_name
492 repo_name_full = repo_name
493
493
494 value['repo_name'] = repo_name
494 value['repo_name'] = repo_name
495 value['repo_name_full'] = repo_name_full
495 value['repo_name_full'] = repo_name_full
496 value['group_path'] = group_path
496 value['group_path'] = group_path
497 value['group_name'] = group_name
497 value['group_name'] = group_name
498 return value
498 return value
499
499
500 def validate_python(self, value, state):
500 def validate_python(self, value, state):
501
501
502 repo_name = value.get('repo_name')
502 repo_name = value.get('repo_name')
503 repo_name_full = value.get('repo_name_full')
503 repo_name_full = value.get('repo_name_full')
504 group_path = value.get('group_path')
504 group_path = value.get('group_path')
505 group_name = value.get('group_name')
505 group_name = value.get('group_name')
506
506
507 if repo_name in [ADMIN_PREFIX, '']:
507 if repo_name in [ADMIN_PREFIX, '']:
508 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
508 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
509 raise formencode.Invalid(
509 raise formencode.Invalid(
510 msg, value, state, error_dict={'repo_name': msg})
510 msg, value, state, error_dict={'repo_name': msg})
511
511
512 rename = old_data.get('repo_name') != repo_name_full
512 rename = old_data.get('repo_name') != repo_name_full
513 create = not edit
513 create = not edit
514 if rename or create:
514 if rename or create:
515
515
516 if group_path:
516 if group_path:
517 if Repository.get_by_repo_name(repo_name_full):
517 if Repository.get_by_repo_name(repo_name_full):
518 msg = M(self, 'repository_in_group_exists', state,
518 msg = M(self, 'repository_in_group_exists', state,
519 repo=repo_name, group=group_name)
519 repo=repo_name, group=group_name)
520 raise formencode.Invalid(
520 raise formencode.Invalid(
521 msg, value, state, error_dict={'repo_name': msg})
521 msg, value, state, error_dict={'repo_name': msg})
522 if RepoGroup.get_by_group_name(repo_name_full):
522 if RepoGroup.get_by_group_name(repo_name_full):
523 msg = M(self, 'group_in_group_exists', state,
523 msg = M(self, 'group_in_group_exists', state,
524 repo=repo_name, group=group_name)
524 repo=repo_name, group=group_name)
525 raise formencode.Invalid(
525 raise formencode.Invalid(
526 msg, value, state, error_dict={'repo_name': msg})
526 msg, value, state, error_dict={'repo_name': msg})
527 else:
527 else:
528 if RepoGroup.get_by_group_name(repo_name_full):
528 if RepoGroup.get_by_group_name(repo_name_full):
529 msg = M(self, 'group_exists', state, repo=repo_name)
529 msg = M(self, 'group_exists', state, repo=repo_name)
530 raise formencode.Invalid(
530 raise formencode.Invalid(
531 msg, value, state, error_dict={'repo_name': msg})
531 msg, value, state, error_dict={'repo_name': msg})
532
532
533 if Repository.get_by_repo_name(repo_name_full):
533 if Repository.get_by_repo_name(repo_name_full):
534 msg = M(
534 msg = M(
535 self, 'repository_exists', state, repo=repo_name)
535 self, 'repository_exists', state, repo=repo_name)
536 raise formencode.Invalid(
536 raise formencode.Invalid(
537 msg, value, state, error_dict={'repo_name': msg})
537 msg, value, state, error_dict={'repo_name': msg})
538 return value
538 return value
539 return _validator
539 return _validator
540
540
541
541
542 def ValidForkName(localizer, *args, **kwargs):
542 def ValidForkName(localizer, *args, **kwargs):
543 _ = localizer
543 _ = localizer
544
544
545 return ValidRepoName(localizer, *args, **kwargs)
545 return ValidRepoName(localizer, *args, **kwargs)
546
546
547
547
548 def SlugifyName(localizer):
548 def SlugifyName(localizer):
549 _ = localizer
549 _ = localizer
550
550
551 class _validator(formencode.validators.FancyValidator):
551 class _validator(formencode.validators.FancyValidator):
552
552
553 def _to_python(self, value, state):
553 def _to_python(self, value, state):
554 return repo_name_slug(value)
554 return repo_name_slug(value)
555
555
556 def validate_python(self, value, state):
556 def validate_python(self, value, state):
557 pass
557 pass
558 return _validator
558 return _validator
559
559
560
560
561 def CannotHaveGitSuffix(localizer):
561 def CannotHaveGitSuffix(localizer):
562 _ = localizer
562 _ = localizer
563
563
564 class _validator(formencode.validators.FancyValidator):
564 class _validator(formencode.validators.FancyValidator):
565 messages = {
565 messages = {
566 'has_git_suffix':
566 'has_git_suffix':
567 _(u'Repository name cannot end with .git'),
567 _(u'Repository name cannot end with .git'),
568 }
568 }
569
569
570 def _to_python(self, value, state):
570 def _to_python(self, value, state):
571 return value
571 return value
572
572
573 def validate_python(self, value, state):
573 def validate_python(self, value, state):
574 if value and value.endswith('.git'):
574 if value and value.endswith('.git'):
575 msg = M(
575 msg = M(
576 self, 'has_git_suffix', state)
576 self, 'has_git_suffix', state)
577 raise formencode.Invalid(
577 raise formencode.Invalid(
578 msg, value, state, error_dict={'repo_name': msg})
578 msg, value, state, error_dict={'repo_name': msg})
579 return _validator
579 return _validator
580
580
581
581
582 def ValidCloneUri(localizer):
582 def ValidCloneUri(localizer):
583 _ = localizer
583 _ = localizer
584
584
585 class InvalidCloneUrl(Exception):
585 class InvalidCloneUrl(Exception):
586 allowed_prefixes = ()
586 allowed_prefixes = ()
587
587
588 def url_handler(repo_type, url):
588 def url_handler(repo_type, url):
589 config = make_db_config(clear_session=False)
589 config = make_db_config(clear_session=False)
590 if repo_type == 'hg':
590 if repo_type == 'hg':
591 allowed_prefixes = ('http', 'svn+http', 'git+http')
591 allowed_prefixes = ('http', 'svn+http', 'git+http')
592
592
593 if 'http' in url[:4]:
593 if 'http' in url[:4]:
594 # initially check if it's at least the proper URL
594 # initially check if it's at least the proper URL
595 # or does it pass basic auth
595 # or does it pass basic auth
596 MercurialRepository.check_url(url, config)
596 MercurialRepository.check_url(url, config)
597 elif 'svn+http' in url[:8]: # svn->hg import
597 elif 'svn+http' in url[:8]: # svn->hg import
598 SubversionRepository.check_url(url, config)
598 SubversionRepository.check_url(url, config)
599 elif 'git+http' in url[:8]: # git->hg import
599 elif 'git+http' in url[:8]: # git->hg import
600 raise NotImplementedError()
600 raise NotImplementedError()
601 else:
601 else:
602 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
602 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
603 'Allowed url must start with one of %s'
603 'Allowed url must start with one of %s'
604 % (url, ','.join(allowed_prefixes)))
604 % (url, ','.join(allowed_prefixes)))
605 exc.allowed_prefixes = allowed_prefixes
605 exc.allowed_prefixes = allowed_prefixes
606 raise exc
606 raise exc
607
607
608 elif repo_type == 'git':
608 elif repo_type == 'git':
609 allowed_prefixes = ('http', 'svn+http', 'hg+http')
609 allowed_prefixes = ('http', 'svn+http', 'hg+http')
610 if 'http' in url[:4]:
610 if 'http' in url[:4]:
611 # initially check if it's at least the proper URL
611 # initially check if it's at least the proper URL
612 # or does it pass basic auth
612 # or does it pass basic auth
613 GitRepository.check_url(url, config)
613 GitRepository.check_url(url, config)
614 elif 'svn+http' in url[:8]: # svn->git import
614 elif 'svn+http' in url[:8]: # svn->git import
615 raise NotImplementedError()
615 raise NotImplementedError()
616 elif 'hg+http' in url[:8]: # hg->git import
616 elif 'hg+http' in url[:8]: # hg->git import
617 raise NotImplementedError()
617 raise NotImplementedError()
618 else:
618 else:
619 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
619 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
620 'Allowed url must start with one of %s'
620 'Allowed url must start with one of %s'
621 % (url, ','.join(allowed_prefixes)))
621 % (url, ','.join(allowed_prefixes)))
622 exc.allowed_prefixes = allowed_prefixes
622 exc.allowed_prefixes = allowed_prefixes
623 raise exc
623 raise exc
624
624
625 class _validator(formencode.validators.FancyValidator):
625 class _validator(formencode.validators.FancyValidator):
626 messages = {
626 messages = {
627 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
627 'clone_uri': _(u'invalid clone url or credentials for %(rtype)s repository'),
628 'invalid_clone_uri': _(
628 'invalid_clone_uri': _(
629 u'Invalid clone url, provide a valid clone '
629 u'Invalid clone url, provide a valid clone '
630 u'url starting with one of %(allowed_prefixes)s')
630 u'url starting with one of %(allowed_prefixes)s')
631 }
631 }
632
632
633 def validate_python(self, value, state):
633 def validate_python(self, value, state):
634 repo_type = value.get('repo_type')
634 repo_type = value.get('repo_type')
635 url = value.get('clone_uri')
635 url = value.get('clone_uri')
636
636
637 if url:
637 if url:
638 try:
638 try:
639 url_handler(repo_type, url)
639 url_handler(repo_type, url)
640 except InvalidCloneUrl as e:
640 except InvalidCloneUrl as e:
641 log.warning(e)
641 log.warning(e)
642 msg = M(self, 'invalid_clone_uri', state, rtype=repo_type,
642 msg = M(self, 'invalid_clone_uri', state, rtype=repo_type,
643 allowed_prefixes=','.join(e.allowed_prefixes))
643 allowed_prefixes=','.join(e.allowed_prefixes))
644 raise formencode.Invalid(msg, value, state,
644 raise formencode.Invalid(msg, value, state,
645 error_dict={'clone_uri': msg})
645 error_dict={'clone_uri': msg})
646 except Exception:
646 except Exception:
647 log.exception('Url validation failed')
647 log.exception('Url validation failed')
648 msg = M(self, 'clone_uri', state, rtype=repo_type)
648 msg = M(self, 'clone_uri', state, rtype=repo_type)
649 raise formencode.Invalid(msg, value, state,
649 raise formencode.Invalid(msg, value, state,
650 error_dict={'clone_uri': msg})
650 error_dict={'clone_uri': msg})
651 return _validator
651 return _validator
652
652
653
653
654 def ValidForkType(localizer, old_data=None):
654 def ValidForkType(localizer, old_data=None):
655 _ = localizer
655 _ = localizer
656 old_data = old_data or {}
656 old_data = old_data or {}
657
657
658 class _validator(formencode.validators.FancyValidator):
658 class _validator(formencode.validators.FancyValidator):
659 messages = {
659 messages = {
660 'invalid_fork_type': _(u'Fork have to be the same type as parent')
660 'invalid_fork_type': _(u'Fork have to be the same type as parent')
661 }
661 }
662
662
663 def validate_python(self, value, state):
663 def validate_python(self, value, state):
664 if old_data['repo_type'] != value:
664 if old_data['repo_type'] != value:
665 msg = M(self, 'invalid_fork_type', state)
665 msg = M(self, 'invalid_fork_type', state)
666 raise formencode.Invalid(
666 raise formencode.Invalid(
667 msg, value, state, error_dict={'repo_type': msg}
667 msg, value, state, error_dict={'repo_type': msg}
668 )
668 )
669 return _validator
669 return _validator
670
670
671
671
672 def CanWriteGroup(localizer, old_data=None):
672 def CanWriteGroup(localizer, old_data=None):
673 _ = localizer
673 _ = localizer
674
674
675 class _validator(formencode.validators.FancyValidator):
675 class _validator(formencode.validators.FancyValidator):
676 messages = {
676 messages = {
677 'permission_denied': _(
677 'permission_denied': _(
678 u"You do not have the permission "
678 u"You do not have the permission "
679 u"to create repositories in this group."),
679 u"to create repositories in this group."),
680 'permission_denied_root': _(
680 'permission_denied_root': _(
681 u"You do not have the permission to store repositories in "
681 u"You do not have the permission to store repositories in "
682 u"the root location.")
682 u"the root location.")
683 }
683 }
684
684
685 def _to_python(self, value, state):
685 def _to_python(self, value, state):
686 # root location
686 # root location
687 if value in [-1, "-1"]:
687 if value in [-1, "-1"]:
688 return None
688 return None
689 return value
689 return value
690
690
691 def validate_python(self, value, state):
691 def validate_python(self, value, state):
692 gr = RepoGroup.get(value)
692 gr = RepoGroup.get(value)
693 gr_name = gr.group_name if gr else None # None means ROOT location
693 gr_name = gr.group_name if gr else None # None means ROOT location
694 # create repositories with write permission on group is set to true
694 # create repositories with write permission on group is set to true
695 create_on_write = HasPermissionAny(
695 create_on_write = HasPermissionAny(
696 'hg.create.write_on_repogroup.true')()
696 'hg.create.write_on_repogroup.true')()
697 group_admin = HasRepoGroupPermissionAny('group.admin')(
697 group_admin = HasRepoGroupPermissionAny('group.admin')(
698 gr_name, 'can write into group validator')
698 gr_name, 'can write into group validator')
699 group_write = HasRepoGroupPermissionAny('group.write')(
699 group_write = HasRepoGroupPermissionAny('group.write')(
700 gr_name, 'can write into group validator')
700 gr_name, 'can write into group validator')
701 forbidden = not (group_admin or (group_write and create_on_write))
701 forbidden = not (group_admin or (group_write and create_on_write))
702 can_create_repos = HasPermissionAny(
702 can_create_repos = HasPermissionAny(
703 'hg.admin', 'hg.create.repository')
703 'hg.admin', 'hg.create.repository')
704 gid = (old_data['repo_group'].get('group_id')
704 gid = (old_data['repo_group'].get('group_id')
705 if (old_data and 'repo_group' in old_data) else None)
705 if (old_data and 'repo_group' in old_data) else None)
706 value_changed = gid != safe_int(value)
706 value_changed = gid != safe_int(value)
707 new = not old_data
707 new = not old_data
708 # do check if we changed the value, there's a case that someone got
708 # do check if we changed the value, there's a case that someone got
709 # revoked write permissions to a repository, he still created, we
709 # revoked write permissions to a repository, he still created, we
710 # don't need to check permission if he didn't change the value of
710 # don't need to check permission if he didn't change the value of
711 # groups in form box
711 # groups in form box
712 if value_changed or new:
712 if value_changed or new:
713 # parent group need to be existing
713 # parent group need to be existing
714 if gr and forbidden:
714 if gr and forbidden:
715 msg = M(self, 'permission_denied', state)
715 msg = M(self, 'permission_denied', state)
716 raise formencode.Invalid(
716 raise formencode.Invalid(
717 msg, value, state, error_dict={'repo_type': msg}
717 msg, value, state, error_dict={'repo_type': msg}
718 )
718 )
719 # check if we can write to root location !
719 # check if we can write to root location !
720 elif gr is None and not can_create_repos():
720 elif gr is None and not can_create_repos():
721 msg = M(self, 'permission_denied_root', state)
721 msg = M(self, 'permission_denied_root', state)
722 raise formencode.Invalid(
722 raise formencode.Invalid(
723 msg, value, state, error_dict={'repo_type': msg}
723 msg, value, state, error_dict={'repo_type': msg}
724 )
724 )
725 return _validator
725 return _validator
726
726
727
727
728 def ValidPerms(localizer, type_='repo'):
728 def ValidPerms(localizer, type_='repo'):
729 _ = localizer
729 _ = localizer
730 if type_ == 'repo_group':
730 if type_ == 'repo_group':
731 EMPTY_PERM = 'group.none'
731 EMPTY_PERM = 'group.none'
732 elif type_ == 'repo':
732 elif type_ == 'repo':
733 EMPTY_PERM = 'repository.none'
733 EMPTY_PERM = 'repository.none'
734 elif type_ == 'user_group':
734 elif type_ == 'user_group':
735 EMPTY_PERM = 'usergroup.none'
735 EMPTY_PERM = 'usergroup.none'
736
736
737 class _validator(formencode.validators.FancyValidator):
737 class _validator(formencode.validators.FancyValidator):
738 messages = {
738 messages = {
739 'perm_new_member_name':
739 'perm_new_member_name':
740 _(u'This username or user group name is not valid')
740 _(u'This username or user group name is not valid')
741 }
741 }
742
742
743 def _to_python(self, value, state):
743 def _to_python(self, value, state):
744 perm_updates = OrderedSet()
744 perm_updates = OrderedSet()
745 perm_additions = OrderedSet()
745 perm_additions = OrderedSet()
746 perm_deletions = OrderedSet()
746 perm_deletions = OrderedSet()
747 # build a list of permission to update/delete and new permission
747 # build a list of permission to update/delete and new permission
748
748
749 # Read the perm_new_member/perm_del_member attributes and group
749 # Read the perm_new_member/perm_del_member attributes and group
750 # them by they IDs
750 # them by they IDs
751 new_perms_group = collections.defaultdict(dict)
751 new_perms_group = collections.defaultdict(dict)
752 del_perms_group = collections.defaultdict(dict)
752 del_perms_group = collections.defaultdict(dict)
753 for k, v in value.copy().iteritems():
753 for k, v in value.copy().iteritems():
754 if k.startswith('perm_del_member'):
754 if k.startswith('perm_del_member'):
755 # delete from org storage so we don't process that later
755 # delete from org storage so we don't process that later
756 del value[k]
756 del value[k]
757 # part is `id`, `type`
757 # part is `id`, `type`
758 _type, part = k.split('perm_del_member_')
758 _type, part = k.split('perm_del_member_')
759 args = part.split('_')
759 args = part.split('_')
760 if len(args) == 2:
760 if len(args) == 2:
761 _key, pos = args
761 _key, pos = args
762 del_perms_group[pos][_key] = v
762 del_perms_group[pos][_key] = v
763 if k.startswith('perm_new_member'):
763 if k.startswith('perm_new_member'):
764 # delete from org storage so we don't process that later
764 # delete from org storage so we don't process that later
765 del value[k]
765 del value[k]
766 # part is `id`, `type`, `perm`
766 # part is `id`, `type`, `perm`
767 _type, part = k.split('perm_new_member_')
767 _type, part = k.split('perm_new_member_')
768 args = part.split('_')
768 args = part.split('_')
769 if len(args) == 2:
769 if len(args) == 2:
770 _key, pos = args
770 _key, pos = args
771 new_perms_group[pos][_key] = v
771 new_perms_group[pos][_key] = v
772
772
773 # store the deletes
773 # store the deletes
774 for k in sorted(del_perms_group.keys()):
774 for k in sorted(del_perms_group.keys()):
775 perm_dict = del_perms_group[k]
775 perm_dict = del_perms_group[k]
776 del_member = perm_dict.get('id')
776 del_member = perm_dict.get('id')
777 del_type = perm_dict.get('type')
777 del_type = perm_dict.get('type')
778 if del_member and del_type:
778 if del_member and del_type:
779 perm_deletions.add(
779 perm_deletions.add(
780 (del_member, None, del_type))
780 (del_member, None, del_type))
781
781
782 # store additions in order of how they were added in web form
782 # store additions in order of how they were added in web form
783 for k in sorted(new_perms_group.keys()):
783 for k in sorted(new_perms_group.keys()):
784 perm_dict = new_perms_group[k]
784 perm_dict = new_perms_group[k]
785 new_member = perm_dict.get('id')
785 new_member = perm_dict.get('id')
786 new_type = perm_dict.get('type')
786 new_type = perm_dict.get('type')
787 new_perm = perm_dict.get('perm')
787 new_perm = perm_dict.get('perm')
788 if new_member and new_perm and new_type:
788 if new_member and new_perm and new_type:
789 perm_additions.add(
789 perm_additions.add(
790 (new_member, new_perm, new_type))
790 (new_member, new_perm, new_type))
791
791
792 # get updates of permissions
792 # get updates of permissions
793 # (read the existing radio button states)
793 # (read the existing radio button states)
794 default_user_id = User.get_default_user_id()
794 default_user_id = User.get_default_user_id()
795
795
796 for k, update_value in value.iteritems():
796 for k, update_value in value.iteritems():
797 if k.startswith('u_perm_') or k.startswith('g_perm_'):
797 if k.startswith('u_perm_') or k.startswith('g_perm_'):
798 obj_type = k[0]
798 obj_type = k[0]
799 obj_id = k[7:]
799 obj_id = k[7:]
800 update_type = {'u': 'user',
800 update_type = {'u': 'user',
801 'g': 'user_group'}[obj_type]
801 'g': 'user_group'}[obj_type]
802
802
803 if obj_type == 'u' and safe_int(obj_id) == default_user_id:
803 if obj_type == 'u' and safe_int(obj_id) == default_user_id:
804 if str2bool(value.get('repo_private')):
804 if str2bool(value.get('repo_private')):
805 # prevent from updating default user permissions
805 # prevent from updating default user permissions
806 # when this repository is marked as private
806 # when this repository is marked as private
807 update_value = EMPTY_PERM
807 update_value = EMPTY_PERM
808
808
809 perm_updates.add(
809 perm_updates.add(
810 (obj_id, update_value, update_type))
810 (obj_id, update_value, update_type))
811
811
812 value['perm_additions'] = [] # propagated later
812 value['perm_additions'] = [] # propagated later
813 value['perm_updates'] = list(perm_updates)
813 value['perm_updates'] = list(perm_updates)
814 value['perm_deletions'] = list(perm_deletions)
814 value['perm_deletions'] = list(perm_deletions)
815
815
816 updates_map = dict(
816 updates_map = dict(
817 (x[0], (x[1], x[2])) for x in value['perm_updates'])
817 (x[0], (x[1], x[2])) for x in value['perm_updates'])
818 # make sure Additions don't override updates.
818 # make sure Additions don't override updates.
819 for member_id, perm, member_type in list(perm_additions):
819 for member_id, perm, member_type in list(perm_additions):
820 if member_id in updates_map:
820 if member_id in updates_map:
821 perm = updates_map[member_id][0]
821 perm = updates_map[member_id][0]
822 value['perm_additions'].append((member_id, perm, member_type))
822 value['perm_additions'].append((member_id, perm, member_type))
823
823
824 # on new entries validate users they exist and they are active !
824 # on new entries validate users they exist and they are active !
825 # this leaves feedback to the form
825 # this leaves feedback to the form
826 try:
826 try:
827 if member_type == 'user':
827 if member_type == 'user':
828 User.query()\
828 User.query()\
829 .filter(User.active == true())\
829 .filter(User.active == true())\
830 .filter(User.user_id == member_id).one()
830 .filter(User.user_id == member_id).one()
831 if member_type == 'user_group':
831 if member_type == 'user_group':
832 UserGroup.query()\
832 UserGroup.query()\
833 .filter(UserGroup.users_group_active == true())\
833 .filter(UserGroup.users_group_active == true())\
834 .filter(UserGroup.users_group_id == member_id)\
834 .filter(UserGroup.users_group_id == member_id)\
835 .one()
835 .one()
836
836
837 except Exception:
837 except Exception:
838 log.exception('Updated permission failed: org_exc:')
838 log.exception('Updated permission failed: org_exc:')
839 msg = M(self, 'perm_new_member_type', state)
839 msg = M(self, 'perm_new_member_type', state)
840 raise formencode.Invalid(
840 raise formencode.Invalid(
841 msg, value, state, error_dict={
841 msg, value, state, error_dict={
842 'perm_new_member_name': msg}
842 'perm_new_member_name': msg}
843 )
843 )
844 return value
844 return value
845 return _validator
845 return _validator
846
846
847
847
848 def ValidPath(localizer):
848 def ValidPath(localizer):
849 _ = localizer
849 _ = localizer
850
850
851 class _validator(formencode.validators.FancyValidator):
851 class _validator(formencode.validators.FancyValidator):
852 messages = {
852 messages = {
853 'invalid_path': _(u'This is not a valid path')
853 'invalid_path': _(u'This is not a valid path')
854 }
854 }
855
855
856 def validate_python(self, value, state):
856 def validate_python(self, value, state):
857 if not os.path.isdir(value):
857 if not os.path.isdir(value):
858 msg = M(self, 'invalid_path', state)
858 msg = M(self, 'invalid_path', state)
859 raise formencode.Invalid(
859 raise formencode.Invalid(
860 msg, value, state, error_dict={'paths_root_path': msg}
860 msg, value, state, error_dict={'paths_root_path': msg}
861 )
861 )
862 return _validator
862 return _validator
863
863
864
864
865 def UniqSystemEmail(localizer, old_data=None):
865 def UniqSystemEmail(localizer, old_data=None):
866 _ = localizer
866 _ = localizer
867 old_data = old_data or {}
867 old_data = old_data or {}
868
868
869 class _validator(formencode.validators.FancyValidator):
869 class _validator(formencode.validators.FancyValidator):
870 messages = {
870 messages = {
871 'email_taken': _(u'This e-mail address is already taken')
871 'email_taken': _(u'This e-mail address is already taken')
872 }
872 }
873
873
874 def _to_python(self, value, state):
874 def _to_python(self, value, state):
875 return value.lower()
875 return value.lower()
876
876
877 def validate_python(self, value, state):
877 def validate_python(self, value, state):
878 if (old_data.get('email') or '').lower() != value:
878 if (old_data.get('email') or '').lower() != value:
879 user = User.get_by_email(value, case_insensitive=True)
879 user = User.get_by_email(value, case_insensitive=True)
880 if user:
880 if user:
881 msg = M(self, 'email_taken', state)
881 msg = M(self, 'email_taken', state)
882 raise formencode.Invalid(
882 raise formencode.Invalid(
883 msg, value, state, error_dict={'email': msg}
883 msg, value, state, error_dict={'email': msg}
884 )
884 )
885 return _validator
885 return _validator
886
886
887
887
888 def ValidSystemEmail(localizer):
888 def ValidSystemEmail(localizer):
889 _ = localizer
889 _ = localizer
890
890
891 class _validator(formencode.validators.FancyValidator):
891 class _validator(formencode.validators.FancyValidator):
892 messages = {
892 messages = {
893 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
893 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
894 }
894 }
895
895
896 def _to_python(self, value, state):
896 def _to_python(self, value, state):
897 return value.lower()
897 return value.lower()
898
898
899 def validate_python(self, value, state):
899 def validate_python(self, value, state):
900 user = User.get_by_email(value, case_insensitive=True)
900 user = User.get_by_email(value, case_insensitive=True)
901 if user is None:
901 if user is None:
902 msg = M(self, 'non_existing_email', state, email=value)
902 msg = M(self, 'non_existing_email', state, email=value)
903 raise formencode.Invalid(
903 raise formencode.Invalid(
904 msg, value, state, error_dict={'email': msg}
904 msg, value, state, error_dict={'email': msg}
905 )
905 )
906 return _validator
906 return _validator
907
907
908
908
909 def NotReviewedRevisions(localizer, repo_id):
909 def NotReviewedRevisions(localizer, repo_id):
910 _ = localizer
910 _ = localizer
911 class _validator(formencode.validators.FancyValidator):
911 class _validator(formencode.validators.FancyValidator):
912 messages = {
912 messages = {
913 'rev_already_reviewed':
913 'rev_already_reviewed':
914 _(u'Revisions %(revs)s are already part of pull request '
914 _(u'Revisions %(revs)s are already part of pull request '
915 u'or have set status'),
915 u'or have set status'),
916 }
916 }
917
917
918 def validate_python(self, value, state):
918 def validate_python(self, value, state):
919 # check revisions if they are not reviewed, or a part of another
919 # check revisions if they are not reviewed, or a part of another
920 # pull request
920 # pull request
921 statuses = ChangesetStatus.query()\
921 statuses = ChangesetStatus.query()\
922 .filter(ChangesetStatus.revision.in_(value))\
922 .filter(ChangesetStatus.revision.in_(value))\
923 .filter(ChangesetStatus.repo_id == repo_id)\
923 .filter(ChangesetStatus.repo_id == repo_id)\
924 .all()
924 .all()
925
925
926 errors = []
926 errors = []
927 for status in statuses:
927 for status in statuses:
928 if status.pull_request_id:
928 if status.pull_request_id:
929 errors.append(['pull_req', status.revision[:12]])
929 errors.append(['pull_req', status.revision[:12]])
930 elif status.status:
930 elif status.status:
931 errors.append(['status', status.revision[:12]])
931 errors.append(['status', status.revision[:12]])
932
932
933 if errors:
933 if errors:
934 revs = ','.join([x[1] for x in errors])
934 revs = ','.join([x[1] for x in errors])
935 msg = M(self, 'rev_already_reviewed', state, revs=revs)
935 msg = M(self, 'rev_already_reviewed', state, revs=revs)
936 raise formencode.Invalid(
936 raise formencode.Invalid(
937 msg, value, state, error_dict={'revisions': revs})
937 msg, value, state, error_dict={'revisions': revs})
938
938
939 return _validator
939 return _validator
940
940
941
941
942 def ValidIp(localizer):
942 def ValidIp(localizer):
943 _ = localizer
943 _ = localizer
944
944
945 class _validator(CIDR):
945 class _validator(CIDR):
946 messages = {
946 messages = {
947 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
947 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
948 'illegalBits': _(
948 'illegalBits': _(
949 u'The network size (bits) must be within the range '
949 u'The network size (bits) must be within the range '
950 u'of 0-32 (not %(bits)r)'),
950 u'of 0-32 (not %(bits)r)'),
951 }
951 }
952
952
953 # we ovveride the default to_python() call
953 # we ovveride the default to_python() call
954 def to_python(self, value, state):
954 def to_python(self, value, state):
955 v = super(_validator, self).to_python(value, state)
955 v = super(_validator, self).to_python(value, state)
956 v = safe_unicode(v.strip())
956 v = safe_unicode(v.strip())
957 net = ipaddress.ip_network(address=v, strict=False)
957 net = ipaddress.ip_network(address=v, strict=False)
958 return str(net)
958 return str(net)
959
959
960 def validate_python(self, value, state):
960 def validate_python(self, value, state):
961 try:
961 try:
962 addr = safe_unicode(value.strip())
962 addr = safe_unicode(value.strip())
963 # this raises an ValueError if address is not IpV4 or IpV6
963 # this raises an ValueError if address is not IpV4 or IpV6
964 ipaddress.ip_network(addr, strict=False)
964 ipaddress.ip_network(addr, strict=False)
965 except ValueError:
965 except ValueError:
966 raise formencode.Invalid(self.message('badFormat', state),
966 raise formencode.Invalid(self.message('badFormat', state),
967 value, state)
967 value, state)
968 return _validator
968 return _validator
969
969
970
970
971 def FieldKey(localizer):
971 def FieldKey(localizer):
972 _ = localizer
972 _ = localizer
973
973
974 class _validator(formencode.validators.FancyValidator):
974 class _validator(formencode.validators.FancyValidator):
975 messages = {
975 messages = {
976 'badFormat': _(
976 'badFormat': _(
977 u'Key name can only consist of letters, '
977 u'Key name can only consist of letters, '
978 u'underscore, dash or numbers'),
978 u'underscore, dash or numbers'),
979 }
979 }
980
980
981 def validate_python(self, value, state):
981 def validate_python(self, value, state):
982 if not re.match('[a-zA-Z0-9_-]+$', value):
982 if not re.match('[a-zA-Z0-9_-]+$', value):
983 raise formencode.Invalid(self.message('badFormat', state),
983 raise formencode.Invalid(self.message('badFormat', state),
984 value, state)
984 value, state)
985 return _validator
985 return _validator
986
986
987
987
988 def ValidAuthPlugins(localizer):
988 def ValidAuthPlugins(localizer):
989 _ = localizer
989 _ = localizer
990
990
991 class _validator(formencode.validators.FancyValidator):
991 class _validator(formencode.validators.FancyValidator):
992 messages = {
992 messages = {
993 'import_duplicate': _(
993 'import_duplicate': _(
994 u'Plugins %(loaded)s and %(next_to_load)s '
994 u'Plugins %(loaded)s and %(next_to_load)s '
995 u'both export the same name'),
995 u'both export the same name'),
996 'missing_includeme': _(
996 'missing_includeme': _(
997 u'The plugin "%(plugin_id)s" is missing an includeme '
997 u'The plugin "%(plugin_id)s" is missing an includeme '
998 u'function.'),
998 u'function.'),
999 'import_error': _(
999 'import_error': _(
1000 u'Can not load plugin "%(plugin_id)s"'),
1000 u'Can not load plugin "%(plugin_id)s"'),
1001 'no_plugin': _(
1001 'no_plugin': _(
1002 u'No plugin available with ID "%(plugin_id)s"'),
1002 u'No plugin available with ID "%(plugin_id)s"'),
1003 }
1003 }
1004
1004
1005 def _to_python(self, value, state):
1005 def _to_python(self, value, state):
1006 # filter empty values
1006 # filter empty values
1007 return filter(lambda s: s not in [None, ''], value)
1007 return filter(lambda s: s not in [None, ''], value)
1008
1008
1009 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1009 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1010 """
1010 """
1011 Validates that the plugin import works. It also checks that the
1011 Validates that the plugin import works. It also checks that the
1012 plugin has an includeme attribute.
1012 plugin has an includeme attribute.
1013 """
1013 """
1014 try:
1014 try:
1015 plugin = _import_legacy_plugin(plugin_id)
1015 plugin = _import_legacy_plugin(plugin_id)
1016 except Exception as e:
1016 except Exception as e:
1017 log.exception(
1017 log.exception(
1018 'Exception during import of auth legacy plugin "{}"'
1018 'Exception during import of auth legacy plugin "{}"'
1019 .format(plugin_id))
1019 .format(plugin_id))
1020 msg = M(self, 'import_error', state, plugin_id=plugin_id)
1020 msg = M(self, 'import_error', state, plugin_id=plugin_id)
1021 raise formencode.Invalid(msg, value, state)
1021 raise formencode.Invalid(msg, value, state)
1022
1022
1023 if not hasattr(plugin, 'includeme'):
1023 if not hasattr(plugin, 'includeme'):
1024 msg = M(self, 'missing_includeme', state, plugin_id=plugin_id)
1024 msg = M(self, 'missing_includeme', state, plugin_id=plugin_id)
1025 raise formencode.Invalid(msg, value, state)
1025 raise formencode.Invalid(msg, value, state)
1026
1026
1027 return plugin
1027 return plugin
1028
1028
1029 def _validate_plugin_id(self, plugin_id, value, state):
1029 def _validate_plugin_id(self, plugin_id, value, state):
1030 """
1030 """
1031 Plugins are already imported during app start up. Therefore this
1031 Plugins are already imported during app start up. Therefore this
1032 validation only retrieves the plugin from the plugin registry and
1032 validation only retrieves the plugin from the plugin registry and
1033 if it returns something not None everything is OK.
1033 if it returns something not None everything is OK.
1034 """
1034 """
1035 plugin = loadplugin(plugin_id)
1035 plugin = loadplugin(plugin_id)
1036
1036
1037 if plugin is None:
1037 if plugin is None:
1038 msg = M(self, 'no_plugin', state, plugin_id=plugin_id)
1038 msg = M(self, 'no_plugin', state, plugin_id=plugin_id)
1039 raise formencode.Invalid(msg, value, state)
1039 raise formencode.Invalid(msg, value, state)
1040
1040
1041 return plugin
1041 return plugin
1042
1042
1043 def validate_python(self, value, state):
1043 def validate_python(self, value, state):
1044 unique_names = {}
1044 unique_names = {}
1045 for plugin_id in value:
1045 for plugin_id in value:
1046
1046
1047 # Validate legacy or normal plugin.
1047 # Validate legacy or normal plugin.
1048 if plugin_id.startswith(legacy_plugin_prefix):
1048 if plugin_id.startswith(legacy_plugin_prefix):
1049 plugin = self._validate_legacy_plugin_id(
1049 plugin = self._validate_legacy_plugin_id(
1050 plugin_id, value, state)
1050 plugin_id, value, state)
1051 else:
1051 else:
1052 plugin = self._validate_plugin_id(plugin_id, value, state)
1052 plugin = self._validate_plugin_id(plugin_id, value, state)
1053
1053
1054 # Only allow unique plugin names.
1054 # Only allow unique plugin names.
1055 if plugin.name in unique_names:
1055 if plugin.name in unique_names:
1056 msg = M(self, 'import_duplicate', state,
1056 msg = M(self, 'import_duplicate', state,
1057 loaded=unique_names[plugin.name],
1057 loaded=unique_names[plugin.name],
1058 next_to_load=plugin)
1058 next_to_load=plugin)
1059 raise formencode.Invalid(msg, value, state)
1059 raise formencode.Invalid(msg, value, state)
1060 unique_names[plugin.name] = plugin
1060 unique_names[plugin.name] = plugin
1061 return _validator
1061 return _validator
1062
1062
1063
1063
1064 def ValidPattern(localizer):
1064 def ValidPattern(localizer):
1065 _ = localizer
1065 _ = localizer
1066
1066
1067 class _validator(formencode.validators.FancyValidator):
1067 class _validator(formencode.validators.FancyValidator):
1068 messages = {
1068 messages = {
1069 'bad_format': _(u'Url must start with http or /'),
1069 'bad_format': _(u'Url must start with http or /'),
1070 }
1070 }
1071
1071
1072 def _to_python(self, value, state):
1072 def _to_python(self, value, state):
1073 patterns = []
1073 patterns = []
1074
1074
1075 prefix = 'new_pattern'
1075 prefix = 'new_pattern'
1076 for name, v in value.iteritems():
1076 for name, v in value.iteritems():
1077 pattern_name = '_'.join((prefix, 'pattern'))
1077 pattern_name = '_'.join((prefix, 'pattern'))
1078 if name.startswith(pattern_name):
1078 if name.startswith(pattern_name):
1079 new_item_id = name[len(pattern_name)+1:]
1079 new_item_id = name[len(pattern_name)+1:]
1080
1080
1081 def _field(name):
1081 def _field(name):
1082 return '%s_%s_%s' % (prefix, name, new_item_id)
1082 return '%s_%s_%s' % (prefix, name, new_item_id)
1083
1083
1084 values = {
1084 values = {
1085 'issuetracker_pat': value.get(_field('pattern')),
1085 'issuetracker_pat': value.get(_field('pattern')),
1086 'issuetracker_url': value.get(_field('url')),
1086 'issuetracker_url': value.get(_field('url')),
1087 'issuetracker_pref': value.get(_field('prefix')),
1087 'issuetracker_pref': value.get(_field('prefix')),
1088 'issuetracker_desc': value.get(_field('description'))
1088 'issuetracker_desc': value.get(_field('description'))
1089 }
1089 }
1090 new_uid = md5(values['issuetracker_pat'])
1090 new_uid = md5(values['issuetracker_pat'])
1091
1091
1092 has_required_fields = (
1092 has_required_fields = (
1093 values['issuetracker_pat']
1093 values['issuetracker_pat']
1094 and values['issuetracker_url'])
1094 and values['issuetracker_url'])
1095
1095
1096 if has_required_fields:
1096 if has_required_fields:
1097 # validate url that it starts with http or /
1097 # validate url that it starts with http or /
1098 # otherwise it can lead to JS injections
1098 # otherwise it can lead to JS injections
1099 # e.g specifig javascript:<malicios code>
1099 # e.g specifig javascript:<malicios code>
1100 if not values['issuetracker_url'].startswith(('http', '/')):
1100 if not values['issuetracker_url'].startswith(('http', '/')):
1101 raise formencode.Invalid(
1101 raise formencode.Invalid(
1102 self.message('bad_format', state),
1102 self.message('bad_format', state),
1103 value, state)
1103 value, state)
1104
1104
1105 settings = [
1105 settings = [
1106 ('_'.join((key, new_uid)), values[key], 'unicode')
1106 ('_'.join((key, new_uid)), values[key], 'unicode')
1107 for key in values]
1107 for key in values]
1108 patterns.append(settings)
1108 patterns.append(settings)
1109
1109
1110 value['patterns'] = patterns
1110 value['patterns'] = patterns
1111 delete_patterns = value.get('uid') or []
1111 delete_patterns = value.get('uid') or []
1112 if not isinstance(delete_patterns, (list, tuple)):
1112 if not isinstance(delete_patterns, (list, tuple)):
1113 delete_patterns = [delete_patterns]
1113 delete_patterns = [delete_patterns]
1114 value['delete_patterns'] = delete_patterns
1114 value['delete_patterns'] = delete_patterns
1115 return value
1115 return value
1116 return _validator
1116 return _validator
General Comments 0
You need to be logged in to leave comments. Login now