##// END OF EJS Templates
release: merge back stable branch into default
marcink -
r2344:e110c80e merge default
parent child Browse files
Show More
@@ -0,0 +1,41 b''
1 |RCE| 4.10.4 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2017-11-14
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18
19
20 Security
21 ^^^^^^^^
22
23
24
25 Performance
26 ^^^^^^^^^^^
27
28
29
30 Fixes
31 ^^^^^
32
33
34 - repository groups: fixed a regressions in update of nested repository groups.
35
36
37 Upgrade notes
38 ^^^^^^^^^^^^^
39
40 - Fixes regression in nested repository groups update. No upgrade problems should
41 be expected
@@ -1,27 +1,28 b''
1 1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
2 2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
3 3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
4 4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
5 5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
6 6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
7 7 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1
8 8 fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0
9 9 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1
10 10 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0
11 11 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1
12 12 d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2
13 13 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0
14 14 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1
15 15 416fec799314c70a5c780fb28b3357b08869333a v4.5.2
16 16 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0
17 17 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1
18 18 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0
19 19 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1
20 20 bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2
21 21 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0
22 22 c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0
23 23 d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1
24 24 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0
25 25 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1
26 26 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2
27 27 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3
28 00821d3afd1dce3f4767cc353f84a17f7d5218a1 v4.10.4
@@ -1,104 +1,105 b''
1 1 .. _rhodecode-release-notes-ref:
2 2
3 3 Release Notes
4 4 =============
5 5
6 6 |RCE| 4.x Versions
7 7 ------------------
8 8
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.10.4.rst
12 13 release-notes-4.10.3.rst
13 14 release-notes-4.10.2.rst
14 15 release-notes-4.10.1.rst
15 16 release-notes-4.10.0.rst
16 17 release-notes-4.9.1.rst
17 18 release-notes-4.9.0.rst
18 19 release-notes-4.8.0.rst
19 20 release-notes-4.7.2.rst
20 21 release-notes-4.7.1.rst
21 22 release-notes-4.7.0.rst
22 23 release-notes-4.6.1.rst
23 24 release-notes-4.6.0.rst
24 25 release-notes-4.5.2.rst
25 26 release-notes-4.5.1.rst
26 27 release-notes-4.5.0.rst
27 28 release-notes-4.4.2.rst
28 29 release-notes-4.4.1.rst
29 30 release-notes-4.4.0.rst
30 31 release-notes-4.3.1.rst
31 32 release-notes-4.3.0.rst
32 33 release-notes-4.2.1.rst
33 34 release-notes-4.2.0.rst
34 35 release-notes-4.1.2.rst
35 36 release-notes-4.1.1.rst
36 37 release-notes-4.1.0.rst
37 38 release-notes-4.0.1.rst
38 39 release-notes-4.0.0.rst
39 40
40 41 |RCE| 3.x Versions
41 42 ------------------
42 43
43 44 .. toctree::
44 45 :maxdepth: 1
45 46
46 47 release-notes-3.8.4.rst
47 48 release-notes-3.8.3.rst
48 49 release-notes-3.8.2.rst
49 50 release-notes-3.8.1.rst
50 51 release-notes-3.8.0.rst
51 52 release-notes-3.7.1.rst
52 53 release-notes-3.7.0.rst
53 54 release-notes-3.6.1.rst
54 55 release-notes-3.6.0.rst
55 56 release-notes-3.5.2.rst
56 57 release-notes-3.5.1.rst
57 58 release-notes-3.5.0.rst
58 59 release-notes-3.4.1.rst
59 60 release-notes-3.4.0.rst
60 61 release-notes-3.3.4.rst
61 62 release-notes-3.3.3.rst
62 63 release-notes-3.3.2.rst
63 64 release-notes-3.3.1.rst
64 65 release-notes-3.3.0.rst
65 66 release-notes-3.2.3.rst
66 67 release-notes-3.2.2.rst
67 68 release-notes-3.2.1.rst
68 69 release-notes-3.2.0.rst
69 70 release-notes-3.1.1.rst
70 71 release-notes-3.1.0.rst
71 72 release-notes-3.0.2.rst
72 73 release-notes-3.0.1.rst
73 74 release-notes-3.0.0.rst
74 75
75 76 |RCE| 2.x Versions
76 77 ------------------
77 78
78 79 .. toctree::
79 80 :maxdepth: 1
80 81
81 82 release-notes-2.2.8.rst
82 83 release-notes-2.2.7.rst
83 84 release-notes-2.2.6.rst
84 85 release-notes-2.2.5.rst
85 86 release-notes-2.2.4.rst
86 87 release-notes-2.2.3.rst
87 88 release-notes-2.2.2.rst
88 89 release-notes-2.2.1.rst
89 90 release-notes-2.2.0.rst
90 91 release-notes-2.1.0.rst
91 92 release-notes-2.0.2.rst
92 93 release-notes-2.0.1.rst
93 94 release-notes-2.0.0.rst
94 95
95 96 |RCE| 1.x Versions
96 97 ------------------
97 98
98 99 .. toctree::
99 100 :maxdepth: 1
100 101
101 102 release-notes-1.7.2.rst
102 103 release-notes-1.7.1.rst
103 104 release-notes-1.7.0.rst
104 105 release-notes-1.6.0.rst
@@ -1,183 +1,183 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 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 logging
22 22 import deform
23 23
24 24 from pyramid.view import view_config
25 25 from pyramid.httpexceptions import HTTPFound
26 26
27 27 from rhodecode.apps._base import RepoGroupAppView
28 28 from rhodecode.forms import RcForm
29 29 from rhodecode.lib import helpers as h
30 30 from rhodecode.lib import audit_logger
31 31 from rhodecode.lib.auth import (
32 32 LoginRequired, HasPermissionAll,
33 33 HasRepoGroupPermissionAny, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
34 34 from rhodecode.model.db import Session, RepoGroup
35 35 from rhodecode.model.scm import RepoGroupList
36 36 from rhodecode.model.repo_group import RepoGroupModel
37 37 from rhodecode.model.validation_schema.schemas import repo_group_schema
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class RepoGroupSettingsView(RepoGroupAppView):
43 43 def load_default_context(self):
44 44 c = self._get_local_tmpl_context()
45 45 c.repo_group = self.db_repo_group
46 46 no_parrent = not c.repo_group.parent_group
47 47 can_create_in_root = self._can_create_repo_group()
48 48
49 49 show_root_location = False
50 50 if no_parrent or can_create_in_root:
51 51 # we're global admin, we're ok and we can create TOP level groups
52 52 # or in case this group is already at top-level we also allow
53 53 # creation in root
54 54 show_root_location = True
55 55
56 56 acl_groups = RepoGroupList(
57 57 RepoGroup.query().all(),
58 58 perm_set=['group.admin'])
59 59 c.repo_groups = RepoGroup.groups_choices(
60 60 groups=acl_groups,
61 61 show_empty_group=show_root_location)
62 62 # filter out current repo group
63 63 exclude_group_ids = [c.repo_group.group_id]
64 64 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
65 65 c.repo_groups)
66 66 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
67 67
68 68 parent_group = c.repo_group.parent_group
69 69
70 70 add_parent_group = (parent_group and (
71 71 parent_group.group_id not in c.repo_groups_choices))
72 72 if add_parent_group:
73 73 c.repo_groups_choices.append(parent_group.group_id)
74 74 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
75 75
76 76 self._register_global_c(c)
77 77 return c
78 78
79 79 def _can_create_repo_group(self, parent_group_id=None):
80 80 is_admin = HasPermissionAll('hg.admin')('group create controller')
81 81 create_repo_group = HasPermissionAll(
82 82 'hg.repogroup.create.true')('group create controller')
83 83 if is_admin or (create_repo_group and not parent_group_id):
84 84 # we're global admin, or we have global repo group create
85 85 # permission
86 86 # we're ok and we can create TOP level groups
87 87 return True
88 88 elif parent_group_id:
89 89 # we check the permission if we can write to parent group
90 90 group = RepoGroup.get(parent_group_id)
91 91 group_name = group.group_name if group else None
92 92 if HasRepoGroupPermissionAny('group.admin')(
93 93 group_name, 'check if user is an admin of group'):
94 94 # we're an admin of passed in group, we're ok.
95 95 return True
96 96 else:
97 97 return False
98 98 return False
99 99
100 100 def _get_schema(self, c, old_values=None):
101 101 return repo_group_schema.RepoGroupSettingsSchema().bind(
102 102 repo_group_repo_group_options=c.repo_groups_choices,
103 103 repo_group_repo_group_items=c.repo_groups,
104 104
105 105 # user caller
106 106 user=self._rhodecode_user,
107 107 old_values=old_values
108 108 )
109 109
110 110 @LoginRequired()
111 111 @HasRepoGroupPermissionAnyDecorator('group.admin')
112 112 @view_config(
113 113 route_name='edit_repo_group', request_method='GET',
114 114 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
115 115 def edit_settings(self):
116 116 c = self.load_default_context()
117 117 c.active = 'settings'
118 118
119 119 defaults = RepoGroupModel()._get_defaults(self.db_repo_group_name)
120 120 defaults['repo_group_owner'] = defaults['user']
121 121
122 122 schema = self._get_schema(c)
123 123 c.form = RcForm(schema, appstruct=defaults)
124 124 return self._get_template_context(c)
125 125
126 126 @LoginRequired()
127 127 @HasRepoGroupPermissionAnyDecorator('group.admin')
128 128 @CSRFRequired()
129 129 @view_config(
130 130 route_name='edit_repo_group', request_method='POST',
131 131 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
132 132 def edit_settings_update(self):
133 133 _ = self.request.translate
134 134 c = self.load_default_context()
135 135 c.active = 'settings'
136 136
137 137 old_repo_group_name = self.db_repo_group_name
138 138 new_repo_group_name = old_repo_group_name
139 139
140 140 old_values = RepoGroupModel()._get_defaults(self.db_repo_group_name)
141 141 schema = self._get_schema(c, old_values=old_values)
142 142
143 143 c.form = RcForm(schema)
144 144 pstruct = self.request.POST.items()
145 145
146 146 try:
147 147 schema_data = c.form.validate(pstruct)
148 148 except deform.ValidationFailure as err_form:
149 149 return self._get_template_context(c)
150 150
151 151 # data is now VALID, proceed with updates
152 152 # save validated data back into the updates dict
153 153 validated_updates = dict(
154 154 group_name=schema_data['repo_group']['repo_group_name_without_group'],
155 155 group_parent_id=schema_data['repo_group']['repo_group_id'],
156 156 user=schema_data['repo_group_owner'],
157 157 group_description=schema_data['repo_group_description'],
158 158 enable_locking=schema_data['repo_group_enable_locking'],
159 159 )
160 160
161 161 try:
162 162 RepoGroupModel().update(self.db_repo_group, validated_updates)
163 163
164 164 audit_logger.store_web(
165 165 'repo_group.edit', action_data={'old_data': old_values},
166 166 user=c.rhodecode_user)
167 167
168 168 Session().commit()
169 169
170 170 # use the new full name for redirect once we know we updated
171 171 # the name on filesystem and in DB
172 new_repo_group_name = schema_data['repo_group_name']
172 new_repo_group_name = schema_data['repo_group']['repo_group_name_with_group']
173 173
174 174 h.flash(_('Repository Group `{}` updated successfully').format(
175 175 old_repo_group_name), category='success')
176 176
177 177 except Exception:
178 178 log.exception("Exception during update or repository group")
179 179 h.flash(_('Error occurred during update of repository group %s')
180 180 % old_repo_group_name, category='error')
181 181
182 182 raise HTTPFound(
183 183 h.route_path('edit_repo_group', repo_group_name=new_repo_group_name))
@@ -1,284 +1,298 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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 25 from rhodecode.translation import _
26 26 from rhodecode.model.validation_schema import validators, preparers, types
27 27
28 28
29 29 def get_group_and_repo(repo_name):
30 30 from rhodecode.model.repo_group import RepoGroupModel
31 31 return RepoGroupModel()._get_group_name_and_parent(
32 32 repo_name, get_object=True)
33 33
34 34
35 def get_repo_group(repo_group_id):
36 from rhodecode.model.repo_group import RepoGroup
37 return RepoGroup.get(repo_group_id), RepoGroup.CHOICES_SEPARATOR
38
39
35 40 @colander.deferred
36 41 def deferred_can_write_to_group_validator(node, kw):
37 42 old_values = kw.get('old_values') or {}
38 43 request_user = kw.get('user')
39 44
40 45 def can_write_group_validator(node, value):
41 46 from rhodecode.lib.auth import (
42 47 HasPermissionAny, HasRepoGroupPermissionAny)
43 48 from rhodecode.model.repo_group import RepoGroupModel
44 49
45 50 messages = {
46 51 'invalid_parent_repo_group':
47 52 _(u"Parent repository group `{}` does not exist"),
48 53 # permissions denied we expose as not existing, to prevent
49 54 # resource discovery
50 55 'permission_denied_parent_group':
51 56 _(u"Parent repository group `{}` does not exist"),
52 57 'permission_denied_root':
53 58 _(u"You do not have the permission to store "
54 59 u"repository groups in the root location.")
55 60 }
56 61
57 62 value = value['repo_group_name']
58 63 parent_group_name = value
59 64
60 65 is_root_location = value is types.RootLocation
61 66
62 67 # NOT initialized validators, we must call them
63 68 can_create_repo_groups_at_root = HasPermissionAny(
64 69 'hg.admin', 'hg.repogroup.create.true')
65 70
66 71 if is_root_location:
67 72 if can_create_repo_groups_at_root(user=request_user):
68 73 # we can create repo group inside tool-level. No more checks
69 74 # are required
70 75 return
71 76 else:
72 77 raise colander.Invalid(node, messages['permission_denied_root'])
73 78
74 79 # check if the parent repo group actually exists
75 80 parent_group = None
76 81 if parent_group_name:
77 82 parent_group = RepoGroupModel().get_by_group_name(parent_group_name)
78 83 if value and not parent_group:
79 84 raise colander.Invalid(
80 85 node, messages['invalid_parent_repo_group'].format(
81 86 parent_group_name))
82 87
83 88 # check if we have permissions to create new groups under
84 89 # parent repo group
85 90 # create repositories with write permission on group is set to true
86 91 create_on_write = HasPermissionAny(
87 92 'hg.create.write_on_repogroup.true')(user=request_user)
88 93
89 94 group_admin = HasRepoGroupPermissionAny('group.admin')(
90 95 parent_group_name, 'can write into group validator', user=request_user)
91 96 group_write = HasRepoGroupPermissionAny('group.write')(
92 97 parent_group_name, 'can write into group validator', user=request_user)
93 98
94 99 # creation by write access is currently disabled. Needs thinking if
95 100 # we want to allow this...
96 101 forbidden = not (group_admin or (group_write and create_on_write and 0))
97 102
98 103 if parent_group and forbidden:
99 104 msg = messages['permission_denied_parent_group'].format(
100 105 parent_group_name)
101 106 raise colander.Invalid(node, msg)
102 107
103 108 return can_write_group_validator
104 109
105 110
106 111 @colander.deferred
107 112 def deferred_repo_group_owner_validator(node, kw):
108 113
109 114 def repo_owner_validator(node, value):
110 115 from rhodecode.model.db import User
111 116 existing = User.get_by_username(value)
112 117 if not existing:
113 118 msg = _(u'Repo group owner with id `{}` does not exists').format(
114 119 value)
115 120 raise colander.Invalid(node, msg)
116 121
117 122 return repo_owner_validator
118 123
119 124
120 125 @colander.deferred
121 126 def deferred_unique_name_validator(node, kw):
122 127 request_user = kw.get('user')
123 128 old_values = kw.get('old_values') or {}
124 129
125 130 def unique_name_validator(node, value):
126 131 from rhodecode.model.db import Repository, RepoGroup
127 132 name_changed = value != old_values.get('group_name')
128 133
129 134 existing = Repository.get_by_repo_name(value)
130 135 if name_changed and existing:
131 136 msg = _(u'Repository with name `{}` already exists').format(value)
132 137 raise colander.Invalid(node, msg)
133 138
134 139 existing_group = RepoGroup.get_by_group_name(value)
135 140 if name_changed and existing_group:
136 141 msg = _(u'Repository group with name `{}` already exists').format(
137 142 value)
138 143 raise colander.Invalid(node, msg)
139 144 return unique_name_validator
140 145
141 146
142 147 @colander.deferred
143 148 def deferred_repo_group_name_validator(node, kw):
144 149 return validators.valid_name_validator
145 150
146 151
147 152 @colander.deferred
148 153 def deferred_repo_group_validator(node, kw):
149 154 options = kw.get(
150 155 'repo_group_repo_group_options')
151 156 return colander.OneOf([x for x in options])
152 157
153 158
154 159 @colander.deferred
155 160 def deferred_repo_group_widget(node, kw):
156 161 items = kw.get('repo_group_repo_group_items')
157 162 return deform.widget.Select2Widget(values=items)
158 163
159 164
160 165 class GroupType(colander.Mapping):
161 166 def _validate(self, node, value):
162 167 try:
163 168 return dict(repo_group_name=value)
164 169 except Exception as e:
165 170 raise colander.Invalid(
166 171 node, '"${val}" is not a mapping type: ${err}'.format(
167 172 val=value, err=e))
168 173
169 174 def deserialize(self, node, cstruct):
170 175 if cstruct is colander.null:
171 176 return cstruct
172 177
173 178 appstruct = super(GroupType, self).deserialize(node, cstruct)
174 179 validated_name = appstruct['repo_group_name']
175 180
176 181 # inject group based on once deserialized data
177 182 (repo_group_name_without_group,
178 183 parent_group_name,
179 184 parent_group) = get_group_and_repo(validated_name)
180 185
186 appstruct['repo_group_name_with_group'] = validated_name
181 187 appstruct['repo_group_name_without_group'] = repo_group_name_without_group
182 188 appstruct['repo_group_name'] = parent_group_name or types.RootLocation
183 189 if parent_group:
184 190 appstruct['repo_group_id'] = parent_group.group_id
185 191
186 192 return appstruct
187 193
188 194
189 195 class GroupSchema(colander.SchemaNode):
190 196 schema_type = GroupType
191 197 validator = deferred_can_write_to_group_validator
192 198 missing = colander.null
193 199
194 200
195 201 class RepoGroup(GroupSchema):
196 202 repo_group_name = colander.SchemaNode(
197 203 types.GroupNameType())
198 204 repo_group_id = colander.SchemaNode(
199 205 colander.String(), missing=None)
200 206 repo_group_name_without_group = colander.SchemaNode(
201 207 colander.String(), missing=None)
202 208
203 209
204 210 class RepoGroupAccessSchema(colander.MappingSchema):
205 211 repo_group = RepoGroup()
206 212
207 213
208 214 class RepoGroupNameUniqueSchema(colander.MappingSchema):
209 215 unique_repo_group_name = colander.SchemaNode(
210 216 colander.String(),
211 217 validator=deferred_unique_name_validator)
212 218
213 219
214 220 class RepoGroupSchema(colander.Schema):
215 221
216 222 repo_group_name = colander.SchemaNode(
217 223 types.GroupNameType(),
218 224 validator=deferred_repo_group_name_validator)
219 225
220 226 repo_group_owner = colander.SchemaNode(
221 227 colander.String(),
222 228 validator=deferred_repo_group_owner_validator)
223 229
224 230 repo_group_description = colander.SchemaNode(
225 231 colander.String(), missing='', widget=deform.widget.TextAreaWidget())
226 232
227 233 repo_group_copy_permissions = colander.SchemaNode(
228 234 types.StringBooleanType(),
229 235 missing=False, widget=deform.widget.CheckboxWidget())
230 236
231 237 repo_group_enable_locking = colander.SchemaNode(
232 238 types.StringBooleanType(),
233 239 missing=False, widget=deform.widget.CheckboxWidget())
234 240
235 241 def deserialize(self, cstruct):
236 242 """
237 243 Custom deserialize that allows to chain validation, and verify
238 244 permissions, and as last step uniqueness
239 245 """
240 246
241 247 appstruct = super(RepoGroupSchema, self).deserialize(cstruct)
242 248 validated_name = appstruct['repo_group_name']
243 249
244 250 # second pass to validate permissions to repo_group
245 251 second = RepoGroupAccessSchema().bind(**self.bindings)
246 252 appstruct_second = second.deserialize({'repo_group': validated_name})
247 253 # save result
248 254 appstruct['repo_group'] = appstruct_second['repo_group']
249 255
250 256 # thirds to validate uniqueness
251 257 third = RepoGroupNameUniqueSchema().bind(**self.bindings)
252 258 third.deserialize({'unique_repo_group_name': validated_name})
253 259
254 260 return appstruct
255 261
256 262
257 263 class RepoGroupSettingsSchema(RepoGroupSchema):
258 264 repo_group = colander.SchemaNode(
259 265 colander.Integer(),
260 266 validator=deferred_repo_group_validator,
261 267 widget=deferred_repo_group_widget,
262 268 missing='')
263 269
264 270 def deserialize(self, cstruct):
265 271 """
266 272 Custom deserialize that allows to chain validation, and verify
267 273 permissions, and as last step uniqueness
268 274 """
269 275
270 276 # first pass, to validate given data
271 277 appstruct = super(RepoGroupSchema, self).deserialize(cstruct)
272 278 validated_name = appstruct['repo_group_name']
273 279
280 # because of repoSchema adds repo-group as an ID, we inject it as
281 # full name here because validators require it, it's unwrapped later
282 # so it's safe to use and final name is going to be without group anyway
283
284 group, separator = get_repo_group(appstruct['repo_group'])
285 if group:
286 validated_name = separator.join([group.group_name, validated_name])
287
274 288 # second pass to validate permissions to repo_group
275 289 second = RepoGroupAccessSchema().bind(**self.bindings)
276 290 appstruct_second = second.deserialize({'repo_group': validated_name})
277 291 # save result
278 292 appstruct['repo_group'] = appstruct_second['repo_group']
279 293
280 294 # thirds to validate uniqueness
281 295 third = RepoGroupNameUniqueSchema().bind(**self.bindings)
282 296 third.deserialize({'unique_repo_group_name': validated_name})
283 297
284 298 return appstruct
@@ -1,116 +1,118 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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 pytest
23 23
24 24 from rhodecode.model.validation_schema import types
25 25 from rhodecode.model.validation_schema.schemas import repo_group_schema
26 26
27 27
28 28 class TestRepoGroupSchema(object):
29 29
30 30 @pytest.mark.parametrize('given, expected', [
31 31 ('my repo', 'my-repo'),
32 32 (' hello world mike ', 'hello-world-mike'),
33 33
34 34 ('//group1/group2//', 'group1/group2'),
35 35 ('//group1///group2//', 'group1/group2'),
36 36 ('///group1/group2///group3', 'group1/group2/group3'),
37 37 ('word g1/group2///group3', 'word-g1/group2/group3'),
38 38
39 39 ('grou p1/gro;,,##up2//.../group3', 'grou-p1/group2/group3'),
40 40
41 41 ('group,,,/,,,/1/2/3', 'group/1/2/3'),
42 42 ('grou[]p1/gro;up2///gro up3', 'group1/group2/gro-up3'),
43 43 (u'grou[]p1/gro;up2///gro up3/Δ…Δ‡', u'group1/group2/gro-up3/Δ…Δ‡'),
44 44 ])
45 45 def test_deserialize_repo_name(self, app, user_admin, given, expected):
46 46 schema = repo_group_schema.RepoGroupSchema().bind()
47 47 assert schema.get('repo_group_name').deserialize(given) == expected
48 48
49 49 def test_deserialize(self, app, user_admin):
50 50 schema = repo_group_schema.RepoGroupSchema().bind(
51 51 user=user_admin
52 52 )
53 53
54 54 schema_data = schema.deserialize(dict(
55 55 repo_group_name='my_schema_group',
56 56 repo_group_owner=user_admin.username
57 57 ))
58 58
59 59 assert schema_data['repo_group_name'] == u'my_schema_group'
60 60 assert schema_data['repo_group'] == {
61 61 'repo_group_id': None,
62 62 'repo_group_name': types.RootLocation,
63 'repo_group_name_with_group': u'my_schema_group',
63 64 'repo_group_name_without_group': u'my_schema_group'}
64 65
65 66 @pytest.mark.parametrize('given, err_key, expected_exc', [
66 67 ('xxx/my_schema_group', 'repo_group', 'Parent repository group `xxx` does not exist'),
67 68 ('', 'repo_group_name', 'Name must start with a letter or number. Got ``'),
68 69 ])
69 70 def test_deserialize_with_bad_group_name(
70 71 self, app, user_admin, given, err_key, expected_exc):
71 72 schema = repo_group_schema.RepoGroupSchema().bind(
72 73 repo_type_options=['hg'],
73 74 user=user_admin
74 75 )
75 76
76 77 with pytest.raises(colander.Invalid) as excinfo:
77 78 schema.deserialize(dict(
78 79 repo_group_name=given,
79 80 repo_group_owner=user_admin.username
80 81 ))
81 82
82 83 assert excinfo.value.asdict()[err_key] == expected_exc
83 84
84 85 def test_deserialize_with_group_name(self, app, user_admin, test_repo_group):
85 86 schema = repo_group_schema.RepoGroupSchema().bind(
86 87 user=user_admin
87 88 )
88 89
89 90 full_name = test_repo_group.group_name + u'/my_schema_group'
90 91 schema_data = schema.deserialize(dict(
91 92 repo_group_name=full_name,
92 93 repo_group_owner=user_admin.username
93 94 ))
94 95
95 96 assert schema_data['repo_group_name'] == full_name
96 97 assert schema_data['repo_group'] == {
97 98 'repo_group_id': test_repo_group.group_id,
98 99 'repo_group_name': test_repo_group.group_name,
100 'repo_group_name_with_group': full_name,
99 101 'repo_group_name_without_group': u'my_schema_group'}
100 102
101 103 def test_deserialize_with_group_name_regular_user_no_perms(
102 104 self, app, user_regular, test_repo_group):
103 105 schema = repo_group_schema.RepoGroupSchema().bind(
104 106 user=user_regular
105 107 )
106 108
107 109 full_name = test_repo_group.group_name + u'/my_schema_group'
108 110 with pytest.raises(colander.Invalid) as excinfo:
109 111 schema.deserialize(dict(
110 112 repo_group_name=full_name,
111 113 repo_group_owner=user_regular.username
112 114 ))
113 115
114 116 expected = 'Parent repository group `{}` does not exist'.format(
115 117 test_repo_group.group_name)
116 118 assert excinfo.value.asdict()['repo_group'] == expected
General Comments 0
You need to be logged in to leave comments. Login now