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