Show More
@@ -80,6 +80,7 b' class RepoSettingsView(RepoAppView):' | |||||
80 |
|
80 | |||
81 | def _get_schema(self, c, old_values=None): |
|
81 | def _get_schema(self, c, old_values=None): | |
82 | return repo_schema.RepoSettingsSchema().bind( |
|
82 | return repo_schema.RepoSettingsSchema().bind( | |
|
83 | repo_type=self.db_repo.repo_type, | |||
83 | repo_type_options=[self.db_repo.repo_type], |
|
84 | repo_type_options=[self.db_repo.repo_type], | |
84 | repo_ref_options=c.landing_revs_choices, |
|
85 | repo_ref_options=c.landing_revs_choices, | |
85 | repo_ref_items=c.landing_revs, |
|
86 | repo_ref_items=c.landing_revs, |
@@ -66,6 +66,13 b' def deferred_landing_ref_validator(node,' | |||||
66 |
|
66 | |||
67 |
|
67 | |||
68 | @colander.deferred |
|
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 | def deferred_landing_ref_widget(node, kw): |
|
76 | def deferred_landing_ref_widget(node, kw): | |
70 | items = kw.get( |
|
77 | items = kw.get( | |
71 | 'repo_ref_items', [(DEFAULT_LANDING_REF, DEFAULT_LANDING_REF)]) |
|
78 | 'repo_ref_items', [(DEFAULT_LANDING_REF, DEFAULT_LANDING_REF)]) | |
@@ -358,3 +365,50 b' class RepoSchema(colander.MappingSchema)' | |||||
358 | third.deserialize({'unique_repo_name': validated_name}) |
|
365 | third.deserialize({'unique_repo_name': validated_name}) | |
359 |
|
366 | |||
360 | return appstruct |
|
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 | import os |
|
21 | import os | |
2 | import re |
|
22 | import re | |
|
23 | import logging | |||
|
24 | ||||
3 |
|
25 | |||
4 | import ipaddress |
|
26 | import ipaddress | |
5 | import colander |
|
27 | import colander | |
@@ -7,6 +29,8 b' import colander' | |||||
7 | from rhodecode.translation import _ |
|
29 | from rhodecode.translation import _ | |
8 | from rhodecode.lib.utils2 import glob2re |
|
30 | from rhodecode.lib.utils2 import glob2re | |
9 |
|
31 | |||
|
32 | log = logging.getLogger(__name__) | |||
|
33 | ||||
10 |
|
34 | |||
11 | def ip_addr_validator(node, value): |
|
35 | def ip_addr_validator(node, value): | |
12 | try: |
|
36 | try: | |
@@ -46,3 +70,71 b' def valid_name_validator(node, value):' | |||||
46 | msg = _('Name must start with a letter or number. Got `{}`').format(value) |
|
70 | msg = _('Name must start with a letter or number. Got `{}`').format(value) | |
47 | if not re.match(r'^[a-zA-z0-9]{1,}', value): |
|
71 | if not re.match(r'^[a-zA-z0-9]{1,}', value): | |
48 | raise colander.Invalid(node, msg) |
|
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 | </div> |
|
52 | </div> | |
53 | <div class="input"> |
|
53 | <div class="input"> | |
54 | %if c.rhodecode_db_repo.clone_uri: |
|
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 | <div id="clone_uri_hidden" class='text-as-placeholder'> | |
57 | <span id="clone_uri_hidden_value">${c.rhodecode_db_repo.clone_uri_hidden}</span> |
|
58 | <span id="clone_uri_hidden_value">${c.rhodecode_db_repo.clone_uri_hidden}</span> | |
58 | <span class="link" id="edit_clone_uri"><i class="icon-edit"></i>${_('edit')}</span> |
|
59 | <span class="link" id="edit_clone_uri"><i class="icon-edit"></i>${_('edit')}</span> | |
59 | </div> |
|
60 | </div> | |
|
61 | % endif | |||
|
62 | ||||
60 | ## alter field |
|
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 | ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri', placeholder=_('enter new value, or leave empty to remove'))|n} |
|
65 | ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri', placeholder=_('enter new value, or leave empty to remove'))|n} | |
63 | ${c.form.render_error(request, c.form['repo_clone_uri'])|n} |
|
66 | ${c.form.render_error(request, c.form['repo_clone_uri'])|n} | |
64 |
|
|
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: | |||
|
71 | ${h.hidden('repo_clone_uri_change', 'OLD')} | |||
|
72 | % endif | |||
65 |
|
73 | |||
|
74 | % if not c.form['repo_clone_uri'].error: | |||
66 | <span class="link" id="cancel_edit_clone_uri">${_('cancel')}</span> |
|
75 | <span class="link" id="cancel_edit_clone_uri">${_('cancel')}</span> | |
|
76 | % endif | |||
|
77 | ||||
67 | </div> |
|
78 | </div> | |
68 | %else: |
|
79 | %else: | |
69 | ## not set yet, display form to set it |
|
80 | ## not set yet, display form to set it |
General Comments 0
You need to be logged in to leave comments.
Login now