Show More
@@ -80,6 +80,7 b' class RepoSettingsView(RepoAppView):' | |||
|
80 | 80 | |
|
81 | 81 | def _get_schema(self, c, old_values=None): |
|
82 | 82 | return repo_schema.RepoSettingsSchema().bind( |
|
83 | repo_type=self.db_repo.repo_type, | |
|
83 | 84 | repo_type_options=[self.db_repo.repo_type], |
|
84 | 85 | repo_ref_options=c.landing_revs_choices, |
|
85 | 86 | repo_ref_items=c.landing_revs, |
@@ -66,6 +66,13 b' def deferred_landing_ref_validator(node,' | |||
|
66 | 66 | |
|
67 | 67 | |
|
68 | 68 | @colander.deferred |
|
69 | def deferred_clone_uri_validator(node, kw): | |
|
70 | repo_type = kw.get('repo_type') | |
|
71 | validator = validators.CloneUriValidator(repo_type) | |
|
72 | return validator | |
|
73 | ||
|
74 | ||
|
75 | @colander.deferred | |
|
69 | 76 | def deferred_landing_ref_widget(node, kw): |
|
70 | 77 | items = kw.get( |
|
71 | 78 | 'repo_ref_items', [(DEFAULT_LANDING_REF, DEFAULT_LANDING_REF)]) |
@@ -358,3 +365,50 b' class RepoSchema(colander.MappingSchema)' | |||
|
358 | 365 | third.deserialize({'unique_repo_name': validated_name}) |
|
359 | 366 | |
|
360 | 367 | return appstruct |
|
368 | ||
|
369 | ||
|
370 | class RepoSettingsSchema(RepoSchema): | |
|
371 | repo_group = colander.SchemaNode( | |
|
372 | colander.Integer(), | |
|
373 | validator=deferred_repo_group_validator, | |
|
374 | widget=deferred_repo_group_widget, | |
|
375 | missing='') | |
|
376 | ||
|
377 | repo_clone_uri_change = colander.SchemaNode( | |
|
378 | colander.String(), | |
|
379 | missing='NEW') | |
|
380 | ||
|
381 | repo_clone_uri = colander.SchemaNode( | |
|
382 | colander.String(), | |
|
383 | preparers=[preparers.strip_preparer], | |
|
384 | validator=deferred_clone_uri_validator, | |
|
385 | missing='') | |
|
386 | ||
|
387 | def deserialize(self, cstruct): | |
|
388 | """ | |
|
389 | Custom deserialize that allows to chain validation, and verify | |
|
390 | permissions, and as last step uniqueness | |
|
391 | """ | |
|
392 | ||
|
393 | # first pass, to validate given data | |
|
394 | appstruct = super(RepoSchema, self).deserialize(cstruct) | |
|
395 | validated_name = appstruct['repo_name'] | |
|
396 | # because of repoSchema adds repo-group as an ID, we inject it as | |
|
397 | # full name here because validators require it, it's unwrapped later | |
|
398 | # so it's safe to use and final name is going to be without group anyway | |
|
399 | ||
|
400 | group, separator = get_repo_group(appstruct['repo_group']) | |
|
401 | if group: | |
|
402 | validated_name = separator.join([group.group_name, validated_name]) | |
|
403 | ||
|
404 | # second pass to validate permissions to repo_group | |
|
405 | second = RepoGroupAccessSchema().bind(**self.bindings) | |
|
406 | appstruct_second = second.deserialize({'repo_group': validated_name}) | |
|
407 | # save result | |
|
408 | appstruct['repo_group'] = appstruct_second['repo_group'] | |
|
409 | ||
|
410 | # thirds to validate uniqueness | |
|
411 | third = RepoNameUniqueSchema().bind(**self.bindings) | |
|
412 | third.deserialize({'unique_repo_name': validated_name}) | |
|
413 | ||
|
414 | return appstruct |
@@ -1,5 +1,27 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2011-2017 RhodeCode GmbH | |
|
4 | # | |
|
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 | |
|
7 | # (only), as published by the Free Software Foundation. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
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/>. | |
|
16 | # | |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
|
20 | ||
|
1 | 21 | import os |
|
2 | 22 | import re |
|
23 | import logging | |
|
24 | ||
|
3 | 25 | |
|
4 | 26 | import ipaddress |
|
5 | 27 | import colander |
@@ -7,6 +29,8 b' import colander' | |||
|
7 | 29 | from rhodecode.translation import _ |
|
8 | 30 | from rhodecode.lib.utils2 import glob2re |
|
9 | 31 | |
|
32 | log = logging.getLogger(__name__) | |
|
33 | ||
|
10 | 34 | |
|
11 | 35 | def ip_addr_validator(node, value): |
|
12 | 36 | try: |
@@ -46,3 +70,71 b' def valid_name_validator(node, value):' | |||
|
46 | 70 | msg = _('Name must start with a letter or number. Got `{}`').format(value) |
|
47 | 71 | if not re.match(r'^[a-zA-z0-9]{1,}', value): |
|
48 | 72 | raise colander.Invalid(node, msg) |
|
73 | ||
|
74 | ||
|
75 | class InvalidCloneUrl(Exception): | |
|
76 | allowed_prefixes = () | |
|
77 | ||
|
78 | ||
|
79 | def url_validator(url, repo_type, config): | |
|
80 | from rhodecode.lib.vcs.backends.hg import MercurialRepository | |
|
81 | from rhodecode.lib.vcs.backends.git import GitRepository | |
|
82 | from rhodecode.lib.vcs.backends.svn import SubversionRepository | |
|
83 | ||
|
84 | if repo_type == 'hg': | |
|
85 | allowed_prefixes = ('http', 'svn+http', 'git+http') | |
|
86 | ||
|
87 | if 'http' in url[:4]: | |
|
88 | # initially check if it's at least the proper URL | |
|
89 | # or does it pass basic auth | |
|
90 | ||
|
91 | MercurialRepository.check_url(url, config) | |
|
92 | elif 'svn+http' in url[:8]: # svn->hg import | |
|
93 | SubversionRepository.check_url(url, config) | |
|
94 | elif 'git+http' in url[:8]: # git->hg import | |
|
95 | raise NotImplementedError() | |
|
96 | else: | |
|
97 | exc = InvalidCloneUrl('Clone from URI %s not allowed. ' | |
|
98 | 'Allowed url must start with one of %s' | |
|
99 | % (url, ','.join(allowed_prefixes))) | |
|
100 | exc.allowed_prefixes = allowed_prefixes | |
|
101 | raise exc | |
|
102 | ||
|
103 | elif repo_type == 'git': | |
|
104 | allowed_prefixes = ('http', 'svn+http', 'hg+http') | |
|
105 | if 'http' in url[:4]: | |
|
106 | # initially check if it's at least the proper URL | |
|
107 | # or does it pass basic auth | |
|
108 | GitRepository.check_url(url, config) | |
|
109 | elif 'svn+http' in url[:8]: # svn->git import | |
|
110 | raise NotImplementedError() | |
|
111 | elif 'hg+http' in url[:8]: # hg->git import | |
|
112 | raise NotImplementedError() | |
|
113 | else: | |
|
114 | exc = InvalidCloneUrl('Clone from URI %s not allowed. ' | |
|
115 | 'Allowed url must start with one of %s' | |
|
116 | % (url, ','.join(allowed_prefixes))) | |
|
117 | exc.allowed_prefixes = allowed_prefixes | |
|
118 | raise exc | |
|
119 | ||
|
120 | ||
|
121 | class CloneUriValidator(object): | |
|
122 | def __init__(self, repo_type): | |
|
123 | self.repo_type = repo_type | |
|
124 | ||
|
125 | def __call__(self, node, value): | |
|
126 | from rhodecode.lib.utils import make_db_config | |
|
127 | try: | |
|
128 | config = make_db_config(clear_session=False) | |
|
129 | url_validator(value, self.repo_type, config) | |
|
130 | except InvalidCloneUrl as e: | |
|
131 | log.warning(e) | |
|
132 | msg = _(u'Invalid clone url, provide a valid clone ' | |
|
133 | u'url starting with one of {allowed_prefixes}').format( | |
|
134 | allowed_prefixes=e.allowed_prefixes) | |
|
135 | raise colander.Invalid(node, msg) | |
|
136 | except Exception: | |
|
137 | log.exception('Url validation failed') | |
|
138 | msg = _(u'invalid clone url for {repo_type} repository').format( | |
|
139 | repo_type=self.repo_type) | |
|
140 | raise colander.Invalid(node, msg) |
@@ -52,18 +52,29 b'' | |||
|
52 | 52 | </div> |
|
53 | 53 | <div class="input"> |
|
54 | 54 | %if c.rhodecode_db_repo.clone_uri: |
|
55 | ## display | |
|
55 | ## display, if we don't have any errors | |
|
56 | % if not c.form['repo_clone_uri'].error: | |
|
56 | 57 |
|
|
57 | 58 | <span id="clone_uri_hidden_value">${c.rhodecode_db_repo.clone_uri_hidden}</span> |
|
58 | 59 | <span class="link" id="edit_clone_uri"><i class="icon-edit"></i>${_('edit')}</span> |
|
59 | 60 | </div> |
|
61 | % endif | |
|
62 | ||
|
60 | 63 | ## alter field |
|
61 | <div id="alter_clone_uri" style="display: none"> | |
|
64 | <div id="alter_clone_uri" style="${'' if c.form['repo_clone_uri'].error else 'display: none'}"> | |
|
62 | 65 | ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri', placeholder=_('enter new value, or leave empty to remove'))|n} |
|
63 | 66 | ${c.form.render_error(request, c.form['repo_clone_uri'])|n} |
|
67 | % if c.form['repo_clone_uri'].error: | |
|
68 | ## we got error from form subit, means we modify the url | |
|
69 | ${h.hidden('repo_clone_uri_change', 'MOD')} | |
|
70 | % else: | |
|
64 | 71 | ${h.hidden('repo_clone_uri_change', 'OLD')} |
|
72 | % endif | |
|
65 | 73 | |
|
74 | % if not c.form['repo_clone_uri'].error: | |
|
66 | 75 | <span class="link" id="cancel_edit_clone_uri">${_('cancel')}</span> |
|
76 | % endif | |
|
77 | ||
|
67 | 78 | </div> |
|
68 | 79 | %else: |
|
69 | 80 | ## not set yet, display form to set it |
General Comments 0
You need to be logged in to leave comments.
Login now