##// END OF EJS Templates
validators: apply username validator to prevent bad values beeing searched in DB, and potential XSS payload sent via validators.
super-admin -
r4706:732ede7c stable
parent child Browse files
Show More
@@ -1,311 +1,313 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import colander
23 23 import deform.widget
24 24
25 from rhodecode.model.validation_schema.utils import username_converter
25 26 from rhodecode.translation import _
26 27 from rhodecode.model.validation_schema import validators, preparers, types
27 28
28 29
29 30 def get_group_and_repo(repo_name):
30 31 from rhodecode.model.repo_group import RepoGroupModel
31 32 return RepoGroupModel()._get_group_name_and_parent(
32 33 repo_name, get_object=True)
33 34
34 35
35 36 def get_repo_group(repo_group_id):
36 37 from rhodecode.model.repo_group import RepoGroup
37 38 return RepoGroup.get(repo_group_id), RepoGroup.CHOICES_SEPARATOR
38 39
39 40
40 41 @colander.deferred
41 42 def deferred_can_write_to_group_validator(node, kw):
42 43 old_values = kw.get('old_values') or {}
43 44 request_user = kw.get('user')
44 45
45 46 def can_write_group_validator(node, value):
46 47 from rhodecode.lib.auth import (
47 48 HasPermissionAny, HasRepoGroupPermissionAny)
48 49 from rhodecode.model.repo_group import RepoGroupModel
49 50
50 51 messages = {
51 52 'invalid_parent_repo_group':
52 53 _(u"Parent repository group `{}` does not exist"),
53 54 # permissions denied we expose as not existing, to prevent
54 55 # resource discovery
55 56 'permission_denied_parent_group':
56 57 _(u"You do not have the permissions to store "
57 58 u"repository groups inside repository group `{}`"),
58 59 'permission_denied_root':
59 60 _(u"You do not have the permission to store "
60 61 u"repository groups in the root location.")
61 62 }
62 63
63 64 value = value['repo_group_name']
64 65 parent_group_name = value
65 66
66 67 is_root_location = value is types.RootLocation
67 68
68 69 # NOT initialized validators, we must call them
69 70 can_create_repo_groups_at_root = HasPermissionAny(
70 71 'hg.admin', 'hg.repogroup.create.true')
71 72
72 73 if is_root_location:
73 74 if can_create_repo_groups_at_root(user=request_user):
74 75 # we can create repo group inside tool-level. No more checks
75 76 # are required
76 77 return
77 78 else:
78 79 raise colander.Invalid(node, messages['permission_denied_root'])
79 80
80 81 # check if the parent repo group actually exists
81 82 parent_group = None
82 83 if parent_group_name:
83 84 parent_group = RepoGroupModel().get_by_group_name(parent_group_name)
84 85 if value and not parent_group:
85 86 raise colander.Invalid(
86 87 node, messages['invalid_parent_repo_group'].format(
87 88 parent_group_name))
88 89
89 90 # check if we have permissions to create new groups under
90 91 # parent repo group
91 92 # create repositories with write permission on group is set to true
92 93 create_on_write = HasPermissionAny(
93 94 'hg.create.write_on_repogroup.true')(user=request_user)
94 95
95 96 group_admin = HasRepoGroupPermissionAny('group.admin')(
96 97 parent_group_name, 'can write into group validator', user=request_user)
97 98 group_write = HasRepoGroupPermissionAny('group.write')(
98 99 parent_group_name, 'can write into group validator', user=request_user)
99 100
100 101 # creation by write access is currently disabled. Needs thinking if
101 102 # we want to allow this...
102 103 forbidden = not (group_admin or (group_write and create_on_write and 0))
103 104
104 105 old_name = old_values.get('group_name')
105 106 if old_name and old_name == old_values.get('submitted_repo_group_name'):
106 107 # we're editing a repository group, we didn't change the name
107 108 # we skip the check for write into parent group now
108 109 # this allows changing settings for this repo group
109 110 return
110 111
111 112 if parent_group and forbidden:
112 113 msg = messages['permission_denied_parent_group'].format(parent_group_name)
113 114 raise colander.Invalid(node, msg)
114 115
115 116 return can_write_group_validator
116 117
117 118
118 119 @colander.deferred
119 120 def deferred_repo_group_owner_validator(node, kw):
120 121
121 122 def repo_owner_validator(node, value):
122 123 from rhodecode.model.db import User
124 value = username_converter(value)
123 125 existing = User.get_by_username(value)
124 126 if not existing:
125 127 msg = _(u'Repo group owner with id `{}` does not exists').format(
126 128 value)
127 129 raise colander.Invalid(node, msg)
128 130
129 131 return repo_owner_validator
130 132
131 133
132 134 @colander.deferred
133 135 def deferred_unique_name_validator(node, kw):
134 136 request_user = kw.get('user')
135 137 old_values = kw.get('old_values') or {}
136 138
137 139 def unique_name_validator(node, value):
138 140 from rhodecode.model.db import Repository, RepoGroup
139 141 name_changed = value != old_values.get('group_name')
140 142
141 143 existing = Repository.get_by_repo_name(value)
142 144 if name_changed and existing:
143 145 msg = _(u'Repository with name `{}` already exists').format(value)
144 146 raise colander.Invalid(node, msg)
145 147
146 148 existing_group = RepoGroup.get_by_group_name(value)
147 149 if name_changed and existing_group:
148 150 msg = _(u'Repository group with name `{}` already exists').format(
149 151 value)
150 152 raise colander.Invalid(node, msg)
151 153 return unique_name_validator
152 154
153 155
154 156 @colander.deferred
155 157 def deferred_repo_group_name_validator(node, kw):
156 158 return validators.valid_name_validator
157 159
158 160
159 161 @colander.deferred
160 162 def deferred_repo_group_validator(node, kw):
161 163 options = kw.get(
162 164 'repo_group_repo_group_options')
163 165 return colander.OneOf([x for x in options])
164 166
165 167
166 168 @colander.deferred
167 169 def deferred_repo_group_widget(node, kw):
168 170 items = kw.get('repo_group_repo_group_items')
169 171 return deform.widget.Select2Widget(values=items)
170 172
171 173
172 174 class GroupType(colander.Mapping):
173 175 def _validate(self, node, value):
174 176 try:
175 177 return dict(repo_group_name=value)
176 178 except Exception as e:
177 179 raise colander.Invalid(
178 180 node, '"${val}" is not a mapping type: ${err}'.format(
179 181 val=value, err=e))
180 182
181 183 def deserialize(self, node, cstruct):
182 184 if cstruct is colander.null:
183 185 return cstruct
184 186
185 187 appstruct = super(GroupType, self).deserialize(node, cstruct)
186 188 validated_name = appstruct['repo_group_name']
187 189
188 190 # inject group based on once deserialized data
189 191 (repo_group_name_without_group,
190 192 parent_group_name,
191 193 parent_group) = get_group_and_repo(validated_name)
192 194
193 195 appstruct['repo_group_name_with_group'] = validated_name
194 196 appstruct['repo_group_name_without_group'] = repo_group_name_without_group
195 197 appstruct['repo_group_name'] = parent_group_name or types.RootLocation
196 198 if parent_group:
197 199 appstruct['repo_group_id'] = parent_group.group_id
198 200
199 201 return appstruct
200 202
201 203
202 204 class GroupSchema(colander.SchemaNode):
203 205 schema_type = GroupType
204 206 validator = deferred_can_write_to_group_validator
205 207 missing = colander.null
206 208
207 209
208 210 class RepoGroup(GroupSchema):
209 211 repo_group_name = colander.SchemaNode(
210 212 types.GroupNameType())
211 213 repo_group_id = colander.SchemaNode(
212 214 colander.String(), missing=None)
213 215 repo_group_name_without_group = colander.SchemaNode(
214 216 colander.String(), missing=None)
215 217
216 218
217 219 class RepoGroupAccessSchema(colander.MappingSchema):
218 220 repo_group = RepoGroup()
219 221
220 222
221 223 class RepoGroupNameUniqueSchema(colander.MappingSchema):
222 224 unique_repo_group_name = colander.SchemaNode(
223 225 colander.String(),
224 226 validator=deferred_unique_name_validator)
225 227
226 228
227 229 class RepoGroupSchema(colander.Schema):
228 230
229 231 repo_group_name = colander.SchemaNode(
230 232 types.GroupNameType(),
231 233 validator=deferred_repo_group_name_validator)
232 234
233 235 repo_group_owner = colander.SchemaNode(
234 236 colander.String(),
235 237 validator=deferred_repo_group_owner_validator)
236 238
237 239 repo_group_description = colander.SchemaNode(
238 240 colander.String(), missing='', widget=deform.widget.TextAreaWidget())
239 241
240 242 repo_group_copy_permissions = colander.SchemaNode(
241 243 types.StringBooleanType(),
242 244 missing=False, widget=deform.widget.CheckboxWidget())
243 245
244 246 repo_group_enable_locking = colander.SchemaNode(
245 247 types.StringBooleanType(),
246 248 missing=False, widget=deform.widget.CheckboxWidget())
247 249
248 250 def deserialize(self, cstruct):
249 251 """
250 252 Custom deserialize that allows to chain validation, and verify
251 253 permissions, and as last step uniqueness
252 254 """
253 255
254 256 appstruct = super(RepoGroupSchema, self).deserialize(cstruct)
255 257 validated_name = appstruct['repo_group_name']
256 258
257 259 # second pass to validate permissions to repo_group
258 260 if 'old_values' in self.bindings:
259 261 # save current repo name for name change checks
260 262 self.bindings['old_values']['submitted_repo_group_name'] = validated_name
261 263 second = RepoGroupAccessSchema().bind(**self.bindings)
262 264 appstruct_second = second.deserialize({'repo_group': validated_name})
263 265 # save result
264 266 appstruct['repo_group'] = appstruct_second['repo_group']
265 267
266 268 # thirds to validate uniqueness
267 269 third = RepoGroupNameUniqueSchema().bind(**self.bindings)
268 270 third.deserialize({'unique_repo_group_name': validated_name})
269 271
270 272 return appstruct
271 273
272 274
273 275 class RepoGroupSettingsSchema(RepoGroupSchema):
274 276 repo_group = colander.SchemaNode(
275 277 colander.Integer(),
276 278 validator=deferred_repo_group_validator,
277 279 widget=deferred_repo_group_widget,
278 280 missing='')
279 281
280 282 def deserialize(self, cstruct):
281 283 """
282 284 Custom deserialize that allows to chain validation, and verify
283 285 permissions, and as last step uniqueness
284 286 """
285 287
286 288 # first pass, to validate given data
287 289 appstruct = super(RepoGroupSchema, self).deserialize(cstruct)
288 290 validated_name = appstruct['repo_group_name']
289 291
290 292 # because of repoSchema adds repo-group as an ID, we inject it as
291 293 # full name here because validators require it, it's unwrapped later
292 294 # so it's safe to use and final name is going to be without group anyway
293 295
294 296 group, separator = get_repo_group(appstruct['repo_group'])
295 297 if group:
296 298 validated_name = separator.join([group.group_name, validated_name])
297 299
298 300 # second pass to validate permissions to repo_group
299 301 if 'old_values' in self.bindings:
300 302 # save current repo name for name change checks
301 303 self.bindings['old_values']['submitted_repo_group_name'] = validated_name
302 304 second = RepoGroupAccessSchema().bind(**self.bindings)
303 305 appstruct_second = second.deserialize({'repo_group': validated_name})
304 306 # save result
305 307 appstruct['repo_group'] = appstruct_second['repo_group']
306 308
307 309 # thirds to validate uniqueness
308 310 third = RepoGroupNameUniqueSchema().bind(**self.bindings)
309 311 third.deserialize({'unique_repo_group_name': validated_name})
310 312
311 313 return appstruct
@@ -1,453 +1,454 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import colander
22 22 import deform.widget
23 23
24 24 from rhodecode.translation import _
25 from rhodecode.model.validation_schema.utils import convert_to_optgroup
25 from rhodecode.model.validation_schema.utils import convert_to_optgroup, username_converter
26 26 from rhodecode.model.validation_schema import validators, preparers, types
27 27
28 28 DEFAULT_LANDING_REF = 'rev:tip'
29 29 DEFAULT_BACKEND_LANDING_REF = {
30 30 'hg': 'branch:default',
31 31 'git': 'branch:master',
32 32 'svn': 'rev:tip',
33 33 }
34 34
35 35
36 36 def get_group_and_repo(repo_name):
37 37 from rhodecode.model.repo_group import RepoGroupModel
38 38 return RepoGroupModel()._get_group_name_and_parent(
39 39 repo_name, get_object=True)
40 40
41 41
42 42 def get_repo_group(repo_group_id):
43 43 from rhodecode.model.repo_group import RepoGroup
44 44 return RepoGroup.get(repo_group_id), RepoGroup.CHOICES_SEPARATOR
45 45
46 46
47 47 @colander.deferred
48 48 def deferred_repo_type_validator(node, kw):
49 49 options = kw.get('repo_type_options', [])
50 50 return colander.OneOf([x for x in options])
51 51
52 52
53 53 @colander.deferred
54 54 def deferred_repo_owner_validator(node, kw):
55 55
56 56 def repo_owner_validator(node, value):
57 57 from rhodecode.model.db import User
58 value = username_converter(value)
58 59 existing = User.get_by_username(value)
59 60 if not existing:
60 61 msg = _(u'Repo owner with id `{}` does not exists').format(value)
61 62 raise colander.Invalid(node, msg)
62 63
63 64 return repo_owner_validator
64 65
65 66
66 67 @colander.deferred
67 68 def deferred_landing_ref_validator(node, kw):
68 69 options = kw.get(
69 70 'repo_ref_options', [DEFAULT_LANDING_REF])
70 71 return colander.OneOf([x for x in options])
71 72
72 73
73 74 @colander.deferred
74 75 def deferred_sync_uri_validator(node, kw):
75 76 repo_type = kw.get('repo_type')
76 77 validator = validators.CloneUriValidator(repo_type)
77 78 return validator
78 79
79 80
80 81 @colander.deferred
81 82 def deferred_landing_ref_widget(node, kw):
82 83 repo_type = kw.get('repo_type')
83 84 default_opts = []
84 85 if repo_type:
85 86 default_opts.append(
86 87 (DEFAULT_BACKEND_LANDING_REF[repo_type],
87 88 DEFAULT_BACKEND_LANDING_REF[repo_type]))
88 89
89 90 items = kw.get('repo_ref_items', default_opts)
90 91 items = convert_to_optgroup(items)
91 92 return deform.widget.Select2Widget(values=items)
92 93
93 94
94 95 @colander.deferred
95 96 def deferred_fork_of_validator(node, kw):
96 97 old_values = kw.get('old_values') or {}
97 98
98 99 def fork_of_validator(node, value):
99 100 from rhodecode.model.db import Repository, RepoGroup
100 101 existing = Repository.get_by_repo_name(value)
101 102 if not existing:
102 103 msg = _(u'Fork with id `{}` does not exists').format(value)
103 104 raise colander.Invalid(node, msg)
104 105 elif old_values['repo_name'] == existing.repo_name:
105 106 msg = _(u'Cannot set fork of '
106 107 u'parameter of this repository to itself').format(value)
107 108 raise colander.Invalid(node, msg)
108 109
109 110 return fork_of_validator
110 111
111 112
112 113 @colander.deferred
113 114 def deferred_can_write_to_group_validator(node, kw):
114 115 request_user = kw.get('user')
115 116 old_values = kw.get('old_values') or {}
116 117
117 118 def can_write_to_group_validator(node, value):
118 119 """
119 120 Checks if given repo path is writable by user. This includes checks if
120 121 user is allowed to create repositories under root path or under
121 122 repo group paths
122 123 """
123 124
124 125 from rhodecode.lib.auth import (
125 126 HasPermissionAny, HasRepoGroupPermissionAny)
126 127 from rhodecode.model.repo_group import RepoGroupModel
127 128
128 129 messages = {
129 130 'invalid_repo_group':
130 131 _(u"Repository group `{}` does not exist"),
131 132 # permissions denied we expose as not existing, to prevent
132 133 # resource discovery
133 134 'permission_denied':
134 135 _(u"Repository group `{}` does not exist"),
135 136 'permission_denied_root':
136 137 _(u"You do not have the permission to store "
137 138 u"repositories in the root location.")
138 139 }
139 140
140 141 value = value['repo_group_name']
141 142
142 143 is_root_location = value is types.RootLocation
143 144 # NOT initialized validators, we must call them
144 145 can_create_repos_at_root = HasPermissionAny('hg.admin', 'hg.create.repository')
145 146
146 147 # if values is root location, we simply need to check if we can write
147 148 # to root location !
148 149 if is_root_location:
149 150
150 151 if can_create_repos_at_root(user=request_user):
151 152 # we can create repo group inside tool-level. No more checks
152 153 # are required
153 154 return
154 155 else:
155 156 old_name = old_values.get('repo_name')
156 157 if old_name and old_name == old_values.get('submitted_repo_name'):
157 158 # since we didn't change the name, we can skip validation and
158 159 # allow current users without store-in-root permissions to update
159 160 return
160 161
161 162 # "fake" node name as repo_name, otherwise we oddly report
162 163 # the error as if it was coming form repo_group
163 164 # however repo_group is empty when using root location.
164 165 node.name = 'repo_name'
165 166 raise colander.Invalid(node, messages['permission_denied_root'])
166 167
167 168 # parent group not exists ? throw an error
168 169 repo_group = RepoGroupModel().get_by_group_name(value)
169 170 if value and not repo_group:
170 171 raise colander.Invalid(
171 172 node, messages['invalid_repo_group'].format(value))
172 173
173 174 gr_name = repo_group.group_name
174 175
175 176 # create repositories with write permission on group is set to true
176 177 create_on_write = HasPermissionAny(
177 178 'hg.create.write_on_repogroup.true')(user=request_user)
178 179
179 180 group_admin = HasRepoGroupPermissionAny('group.admin')(
180 181 gr_name, 'can write into group validator', user=request_user)
181 182 group_write = HasRepoGroupPermissionAny('group.write')(
182 183 gr_name, 'can write into group validator', user=request_user)
183 184
184 185 forbidden = not (group_admin or (group_write and create_on_write))
185 186
186 187 # TODO: handling of old values, and detecting no-change in path
187 188 # to skip permission checks in such cases. This only needs to be
188 189 # implemented if we use this schema in forms as well
189 190
190 191 # gid = (old_data['repo_group'].get('group_id')
191 192 # if (old_data and 'repo_group' in old_data) else None)
192 193 # value_changed = gid != safe_int(value)
193 194 # new = not old_data
194 195
195 196 # do check if we changed the value, there's a case that someone got
196 197 # revoked write permissions to a repository, he still created, we
197 198 # don't need to check permission if he didn't change the value of
198 199 # groups in form box
199 200 # if value_changed or new:
200 201 # # parent group need to be existing
201 202 # TODO: ENDS HERE
202 203
203 204 if repo_group and forbidden:
204 205 msg = messages['permission_denied'].format(value)
205 206 raise colander.Invalid(node, msg)
206 207
207 208 return can_write_to_group_validator
208 209
209 210
210 211 @colander.deferred
211 212 def deferred_unique_name_validator(node, kw):
212 213 request_user = kw.get('user')
213 214 old_values = kw.get('old_values') or {}
214 215
215 216 def unique_name_validator(node, value):
216 217 from rhodecode.model.db import Repository, RepoGroup
217 218 name_changed = value != old_values.get('repo_name')
218 219
219 220 existing = Repository.get_by_repo_name(value)
220 221 if name_changed and existing:
221 222 msg = _(u'Repository with name `{}` already exists').format(value)
222 223 raise colander.Invalid(node, msg)
223 224
224 225 existing_group = RepoGroup.get_by_group_name(value)
225 226 if name_changed and existing_group:
226 227 msg = _(u'Repository group with name `{}` already exists').format(
227 228 value)
228 229 raise colander.Invalid(node, msg)
229 230 return unique_name_validator
230 231
231 232
232 233 @colander.deferred
233 234 def deferred_repo_name_validator(node, kw):
234 235 def no_git_suffix_validator(node, value):
235 236 if value.endswith('.git'):
236 237 msg = _('Repository name cannot end with .git')
237 238 raise colander.Invalid(node, msg)
238 239 return colander.All(
239 240 no_git_suffix_validator, validators.valid_name_validator)
240 241
241 242
242 243 @colander.deferred
243 244 def deferred_repo_group_validator(node, kw):
244 245 options = kw.get(
245 246 'repo_repo_group_options')
246 247 return colander.OneOf([x for x in options])
247 248
248 249
249 250 @colander.deferred
250 251 def deferred_repo_group_widget(node, kw):
251 252 items = kw.get('repo_repo_group_items')
252 253 return deform.widget.Select2Widget(values=items)
253 254
254 255
255 256 class GroupType(colander.Mapping):
256 257 def _validate(self, node, value):
257 258 try:
258 259 return dict(repo_group_name=value)
259 260 except Exception as e:
260 261 raise colander.Invalid(
261 262 node, '"${val}" is not a mapping type: ${err}'.format(
262 263 val=value, err=e))
263 264
264 265 def deserialize(self, node, cstruct):
265 266 if cstruct is colander.null:
266 267 return cstruct
267 268
268 269 appstruct = super(GroupType, self).deserialize(node, cstruct)
269 270 validated_name = appstruct['repo_group_name']
270 271
271 272 # inject group based on once deserialized data
272 273 (repo_name_without_group,
273 274 parent_group_name,
274 275 parent_group) = get_group_and_repo(validated_name)
275 276
276 277 appstruct['repo_name_with_group'] = validated_name
277 278 appstruct['repo_name_without_group'] = repo_name_without_group
278 279 appstruct['repo_group_name'] = parent_group_name or types.RootLocation
279 280
280 281 if parent_group:
281 282 appstruct['repo_group_id'] = parent_group.group_id
282 283
283 284 return appstruct
284 285
285 286
286 287 class GroupSchema(colander.SchemaNode):
287 288 schema_type = GroupType
288 289 validator = deferred_can_write_to_group_validator
289 290 missing = colander.null
290 291
291 292
292 293 class RepoGroup(GroupSchema):
293 294 repo_group_name = colander.SchemaNode(
294 295 types.GroupNameType())
295 296 repo_group_id = colander.SchemaNode(
296 297 colander.String(), missing=None)
297 298 repo_name_without_group = colander.SchemaNode(
298 299 colander.String(), missing=None)
299 300
300 301
301 302 class RepoGroupAccessSchema(colander.MappingSchema):
302 303 repo_group = RepoGroup()
303 304
304 305
305 306 class RepoNameUniqueSchema(colander.MappingSchema):
306 307 unique_repo_name = colander.SchemaNode(
307 308 colander.String(),
308 309 validator=deferred_unique_name_validator)
309 310
310 311
311 312 class RepoSchema(colander.MappingSchema):
312 313
313 314 repo_name = colander.SchemaNode(
314 315 types.RepoNameType(),
315 316 validator=deferred_repo_name_validator)
316 317
317 318 repo_type = colander.SchemaNode(
318 319 colander.String(),
319 320 validator=deferred_repo_type_validator)
320 321
321 322 repo_owner = colander.SchemaNode(
322 323 colander.String(),
323 324 validator=deferred_repo_owner_validator,
324 325 widget=deform.widget.TextInputWidget())
325 326
326 327 repo_description = colander.SchemaNode(
327 328 colander.String(), missing='',
328 329 widget=deform.widget.TextAreaWidget())
329 330
330 331 repo_landing_commit_ref = colander.SchemaNode(
331 332 colander.String(),
332 333 validator=deferred_landing_ref_validator,
333 334 preparers=[preparers.strip_preparer],
334 335 missing=DEFAULT_LANDING_REF,
335 336 widget=deferred_landing_ref_widget)
336 337
337 338 repo_clone_uri = colander.SchemaNode(
338 339 colander.String(),
339 340 validator=deferred_sync_uri_validator,
340 341 preparers=[preparers.strip_preparer],
341 342 missing='')
342 343
343 344 repo_push_uri = colander.SchemaNode(
344 345 colander.String(),
345 346 validator=deferred_sync_uri_validator,
346 347 preparers=[preparers.strip_preparer],
347 348 missing='')
348 349
349 350 repo_fork_of = colander.SchemaNode(
350 351 colander.String(),
351 352 validator=deferred_fork_of_validator,
352 353 missing=None)
353 354
354 355 repo_private = colander.SchemaNode(
355 356 types.StringBooleanType(),
356 357 missing=False, widget=deform.widget.CheckboxWidget())
357 358 repo_copy_permissions = colander.SchemaNode(
358 359 types.StringBooleanType(),
359 360 missing=False, widget=deform.widget.CheckboxWidget())
360 361 repo_enable_statistics = colander.SchemaNode(
361 362 types.StringBooleanType(),
362 363 missing=False, widget=deform.widget.CheckboxWidget())
363 364 repo_enable_downloads = colander.SchemaNode(
364 365 types.StringBooleanType(),
365 366 missing=False, widget=deform.widget.CheckboxWidget())
366 367 repo_enable_locking = colander.SchemaNode(
367 368 types.StringBooleanType(),
368 369 missing=False, widget=deform.widget.CheckboxWidget())
369 370
370 371 def deserialize(self, cstruct):
371 372 """
372 373 Custom deserialize that allows to chain validation, and verify
373 374 permissions, and as last step uniqueness
374 375 """
375 376
376 377 # first pass, to validate given data
377 378 appstruct = super(RepoSchema, self).deserialize(cstruct)
378 379 validated_name = appstruct['repo_name']
379 380
380 381 # second pass to validate permissions to repo_group
381 382 if 'old_values' in self.bindings:
382 383 # save current repo name for name change checks
383 384 self.bindings['old_values']['submitted_repo_name'] = validated_name
384 385 second = RepoGroupAccessSchema().bind(**self.bindings)
385 386 appstruct_second = second.deserialize({'repo_group': validated_name})
386 387 # save result
387 388 appstruct['repo_group'] = appstruct_second['repo_group']
388 389
389 390 # thirds to validate uniqueness
390 391 third = RepoNameUniqueSchema().bind(**self.bindings)
391 392 third.deserialize({'unique_repo_name': validated_name})
392 393
393 394 return appstruct
394 395
395 396
396 397 class RepoSettingsSchema(RepoSchema):
397 398 repo_group = colander.SchemaNode(
398 399 colander.Integer(),
399 400 validator=deferred_repo_group_validator,
400 401 widget=deferred_repo_group_widget,
401 402 missing='')
402 403
403 404 repo_clone_uri_change = colander.SchemaNode(
404 405 colander.String(),
405 406 missing='NEW')
406 407
407 408 repo_clone_uri = colander.SchemaNode(
408 409 colander.String(),
409 410 preparers=[preparers.strip_preparer],
410 411 validator=deferred_sync_uri_validator,
411 412 missing='')
412 413
413 414 repo_push_uri_change = colander.SchemaNode(
414 415 colander.String(),
415 416 missing='NEW')
416 417
417 418 repo_push_uri = colander.SchemaNode(
418 419 colander.String(),
419 420 preparers=[preparers.strip_preparer],
420 421 validator=deferred_sync_uri_validator,
421 422 missing='')
422 423
423 424 def deserialize(self, cstruct):
424 425 """
425 426 Custom deserialize that allows to chain validation, and verify
426 427 permissions, and as last step uniqueness
427 428 """
428 429
429 430 # first pass, to validate given data
430 431 appstruct = super(RepoSchema, self).deserialize(cstruct)
431 432 validated_name = appstruct['repo_name']
432 433 # because of repoSchema adds repo-group as an ID, we inject it as
433 434 # full name here because validators require it, it's unwrapped later
434 435 # so it's safe to use and final name is going to be without group anyway
435 436
436 437 group, separator = get_repo_group(appstruct['repo_group'])
437 438 if group:
438 439 validated_name = separator.join([group.group_name, validated_name])
439 440
440 441 # second pass to validate permissions to repo_group
441 442 if 'old_values' in self.bindings:
442 443 # save current repo name for name change checks
443 444 self.bindings['old_values']['submitted_repo_name'] = validated_name
444 445 second = RepoGroupAccessSchema().bind(**self.bindings)
445 446 appstruct_second = second.deserialize({'repo_group': validated_name})
446 447 # save result
447 448 appstruct['repo_group'] = appstruct_second['repo_group']
448 449
449 450 # thirds to validate uniqueness
450 451 third = RepoNameUniqueSchema().bind(**self.bindings)
451 452 third.deserialize({'unique_repo_name': validated_name})
452 453
453 454 return appstruct
@@ -1,78 +1,80 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 import re
21 21 import colander
22 22
23 23 from rhodecode.model.validation_schema import types, validators
24 from rhodecode.model.validation_schema.utils import username_converter
24 25 from rhodecode.translation import _
25 26
26 27
27 28 @colander.deferred
28 29 def deferred_user_group_name_validator(node, kw):
29 30
30 31 def name_validator(node, value):
31 32
32 33 msg = _('Allowed in name are letters, numbers, and `-`, `_`, `.` '
33 34 'Name must start with a letter or number. Got `{}`').format(value)
34 35
35 36 if not re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value):
36 37 raise colander.Invalid(node, msg)
37 38
38 39 return name_validator
39 40
40 41
41 42 @colander.deferred
42 43 def deferred_user_group_owner_validator(node, kw):
43 44
44 45 def owner_validator(node, value):
45 46 from rhodecode.model.db import User
47 value = username_converter(value)
46 48 existing = User.get_by_username(value)
47 49 if not existing:
48 50 msg = _(u'User group owner with id `{}` does not exists').format(value)
49 51 raise colander.Invalid(node, msg)
50 52
51 53 return owner_validator
52 54
53 55
54 56 class UserGroupSchema(colander.Schema):
55 57
56 58 user_group_name = colander.SchemaNode(
57 59 colander.String(),
58 60 validator=deferred_user_group_name_validator)
59 61
60 62 user_group_description = colander.SchemaNode(
61 63 colander.String(), missing='')
62 64
63 65 user_group_owner = colander.SchemaNode(
64 66 colander.String(),
65 67 validator=deferred_user_group_owner_validator)
66 68
67 69 user_group_active = colander.SchemaNode(
68 70 types.StringBooleanType(),
69 71 missing=False)
70 72
71 73 def deserialize(self, cstruct):
72 74 """
73 75 Custom deserialize that allows to chain validation, and verify
74 76 permissions, and as last step uniqueness
75 77 """
76 78
77 79 appstruct = super(UserGroupSchema, self).deserialize(cstruct)
78 80 return appstruct
@@ -1,49 +1,56 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import deform.widget
22 22
23 23
24 24 def convert_to_optgroup(items):
25 25 """
26 26 Convert such format::
27 27
28 28 [
29 29 ['rev:tip', u'latest tip'],
30 30 ([(u'branch:default', u'default')], u'Branches'),
31 31 ]
32 32
33 33 into one used by deform Select widget::
34 34
35 35 (
36 36 ('rev:tip', 'latest tip'),
37 37 OptGroup('Branches',
38 38 ('branch:default', 'default'),
39 39 )
40 40 """
41 41 result = []
42 42 for value, label in items:
43 43 # option group
44 44 if isinstance(value, (tuple, list)):
45 45 result.append(deform.widget.OptGroup(label, *value))
46 46 else:
47 47 result.append((value, label))
48 48
49 49 return result
50
51
52 def username_converter(value):
53 for noise in ('/', ',', '*', '"', "'", '<', '>', '(', ')', '[', ']', ';'):
54 value = value.replace(noise, '')
55
56 return value
General Comments 0
You need to be logged in to leave comments. Login now