##// END OF EJS Templates
repo-group-schemas: refactor repository group schemas....
marcink -
r1154:e3bdce95 default
parent child Browse files
Show More
@@ -0,0 +1,116 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 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
21 import colander
22 import pytest
23
24 from rhodecode.model.validation_schema import types
25 from rhodecode.model.validation_schema.schemas import repo_group_schema
26
27
28 class TestRepoGroupSchema(object):
29
30 @pytest.mark.parametrize('given, expected', [
31 ('my repo', 'my-repo'),
32 (' hello world mike ', 'hello-world-mike'),
33
34 ('//group1/group2//', 'group1/group2'),
35 ('//group1///group2//', 'group1/group2'),
36 ('///group1/group2///group3', 'group1/group2/group3'),
37 ('word g1/group2///group3', 'word-g1/group2/group3'),
38
39 ('grou p1/gro;,,##up2//.../group3', 'grou-p1/group2/group3'),
40
41 ('group,,,/,,,/1/2/3', 'group/1/2/3'),
42 ('grou[]p1/gro;up2///gro up3', 'group1/group2/gro-up3'),
43 (u'grou[]p1/gro;up2///gro up3/Δ…Δ‡', u'group1/group2/gro-up3/Δ…Δ‡'),
44 ])
45 def test_deserialize_repo_name(self, app, user_admin, given, expected):
46 schema = repo_group_schema.RepoGroupSchema().bind()
47 assert schema.get('repo_group_name').deserialize(given) == expected
48
49 def test_deserialize(self, app, user_admin):
50 schema = repo_group_schema.RepoGroupSchema().bind(
51 user=user_admin
52 )
53
54 schema_data = schema.deserialize(dict(
55 repo_group_name='dupa',
56 repo_group_owner=user_admin.username
57 ))
58
59 assert schema_data['repo_group_name'] == 'dupa'
60 assert schema_data['repo_group'] == {
61 'repo_group_id': None,
62 'repo_group_name': types.RootLocation,
63 'repo_group_name_without_group': 'dupa'}
64
65 @pytest.mark.parametrize('given, err_key, expected_exc', [
66 ('xxx/dupa', 'repo_group', 'Parent repository group `xxx` does not exist'),
67 ('', 'repo_group_name', 'Name must start with a letter or number. Got ``'),
68 ])
69 def test_deserialize_with_bad_group_name(
70 self, app, user_admin, given, err_key, expected_exc):
71 schema = repo_group_schema.RepoGroupSchema().bind(
72 repo_type_options=['hg'],
73 user=user_admin
74 )
75
76 with pytest.raises(colander.Invalid) as excinfo:
77 schema.deserialize(dict(
78 repo_group_name=given,
79 repo_group_owner=user_admin.username
80 ))
81
82 assert excinfo.value.asdict()[err_key] == expected_exc
83
84 def test_deserialize_with_group_name(self, app, user_admin, test_repo_group):
85 schema = repo_group_schema.RepoGroupSchema().bind(
86 user=user_admin
87 )
88
89 full_name = test_repo_group.group_name + '/dupa'
90 schema_data = schema.deserialize(dict(
91 repo_group_name=full_name,
92 repo_group_owner=user_admin.username
93 ))
94
95 assert schema_data['repo_group_name'] == full_name
96 assert schema_data['repo_group'] == {
97 'repo_group_id': test_repo_group.group_id,
98 'repo_group_name': test_repo_group.group_name,
99 'repo_group_name_without_group': 'dupa'}
100
101 def test_deserialize_with_group_name_regular_user_no_perms(
102 self, app, user_regular, test_repo_group):
103 schema = repo_group_schema.RepoGroupSchema().bind(
104 user=user_regular
105 )
106
107 full_name = test_repo_group.group_name + '/dupa'
108 with pytest.raises(colander.Invalid) as excinfo:
109 schema.deserialize(dict(
110 repo_group_name=full_name,
111 repo_group_owner=user_regular.username
112 ))
113
114 expected = 'Parent repository group `{}` does not exist'.format(
115 test_repo_group.group_name)
116 assert excinfo.value.asdict()['repo_group'] == expected
@@ -1,199 +1,289 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 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.repo_group import RepoGroupModel
25 from rhodecode.model.repo_group import RepoGroupModel
26 from rhodecode.model.user import UserModel
26 from rhodecode.model.user import UserModel
27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
28 from rhodecode.api.tests.utils import (
28 from rhodecode.api.tests.utils import (
29 build_data, api_call, assert_ok, assert_error, crash)
29 build_data, api_call, assert_ok, assert_error, crash)
30 from rhodecode.tests.fixture import Fixture
30 from rhodecode.tests.fixture import Fixture
31
31
32
32
33 fixture = Fixture()
33 fixture = Fixture()
34
34
35
35
36 @pytest.mark.usefixtures("testuser_api", "app")
36 @pytest.mark.usefixtures("testuser_api", "app")
37 class TestCreateRepoGroup(object):
37 class TestCreateRepoGroup(object):
38 def test_api_create_repo_group(self):
38 def test_api_create_repo_group(self):
39 repo_group_name = 'api-repo-group'
39 repo_group_name = 'api-repo-group'
40
40
41 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
41 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
42 assert repo_group is None
42 assert repo_group is None
43
43
44 id_, params = build_data(
44 id_, params = build_data(
45 self.apikey, 'create_repo_group',
45 self.apikey, 'create_repo_group',
46 group_name=repo_group_name,
46 group_name=repo_group_name,
47 owner=TEST_USER_ADMIN_LOGIN,)
47 owner=TEST_USER_ADMIN_LOGIN,)
48 response = api_call(self.app, params)
48 response = api_call(self.app, params)
49
49
50 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
50 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
51 assert repo_group is not None
51 assert repo_group is not None
52 ret = {
52 ret = {
53 'msg': 'Created new repo group `%s`' % (repo_group_name,),
53 'msg': 'Created new repo group `%s`' % (repo_group_name,),
54 'repo_group': repo_group.get_api_data()
54 'repo_group': repo_group.get_api_data()
55 }
55 }
56 expected = ret
56 expected = ret
57 try:
57 assert_ok(id_, expected, given=response.body)
58 assert_ok(id_, expected, given=response.body)
59 finally:
58 fixture.destroy_repo_group(repo_group_name)
60 fixture.destroy_repo_group(repo_group_name)
59
61
60 def test_api_create_repo_group_regular_user(self):
61 repo_group_name = 'api-repo-group'
62
63 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
64 usr.inherit_default_permissions = False
65 Session().add(usr)
66 UserModel().grant_perm(
67 self.TEST_USER_LOGIN, 'hg.repogroup.create.true')
68 Session().commit()
69
70 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
71 assert repo_group is None
72
73 id_, params = build_data(
74 self.apikey_regular, 'create_repo_group',
75 group_name=repo_group_name,
76 owner=TEST_USER_ADMIN_LOGIN,)
77 response = api_call(self.app, params)
78
79 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
80 assert repo_group is not None
81 ret = {
82 'msg': 'Created new repo group `%s`' % (repo_group_name,),
83 'repo_group': repo_group.get_api_data()
84 }
85 expected = ret
86 assert_ok(id_, expected, given=response.body)
87 fixture.destroy_repo_group(repo_group_name)
88 UserModel().revoke_perm(
89 self.TEST_USER_LOGIN, 'hg.repogroup.create.true')
90 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
91 usr.inherit_default_permissions = True
92 Session().add(usr)
93 Session().commit()
94
95 def test_api_create_repo_group_regular_user_no_permission(self):
96 repo_group_name = 'api-repo-group'
97
98 id_, params = build_data(
99 self.apikey_regular, 'create_repo_group',
100 group_name=repo_group_name,
101 owner=TEST_USER_ADMIN_LOGIN,)
102 response = api_call(self.app, params)
103
104 expected = "Access was denied to this resource."
105 assert_error(id_, expected, given=response.body)
106
107 def test_api_create_repo_group_in_another_group(self):
62 def test_api_create_repo_group_in_another_group(self):
108 repo_group_name = 'api-repo-group'
63 repo_group_name = 'api-repo-group'
109
64
110 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
65 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
111 assert repo_group is None
66 assert repo_group is None
112 # create the parent
67 # create the parent
113 fixture.create_repo_group(repo_group_name)
68 fixture.create_repo_group(repo_group_name)
114
69
115 full_repo_group_name = repo_group_name+'/'+repo_group_name
70 full_repo_group_name = repo_group_name+'/'+repo_group_name
116 id_, params = build_data(
71 id_, params = build_data(
117 self.apikey, 'create_repo_group',
72 self.apikey, 'create_repo_group',
118 group_name=full_repo_group_name,
73 group_name=full_repo_group_name,
119 owner=TEST_USER_ADMIN_LOGIN,
74 owner=TEST_USER_ADMIN_LOGIN,
120 copy_permissions=True)
75 copy_permissions=True)
121 response = api_call(self.app, params)
76 response = api_call(self.app, params)
122
77
123 repo_group = RepoGroupModel.cls.get_by_group_name(full_repo_group_name)
78 repo_group = RepoGroupModel.cls.get_by_group_name(full_repo_group_name)
124 assert repo_group is not None
79 assert repo_group is not None
125 ret = {
80 ret = {
126 'msg': 'Created new repo group `%s`' % (full_repo_group_name,),
81 'msg': 'Created new repo group `%s`' % (full_repo_group_name,),
127 'repo_group': repo_group.get_api_data()
82 'repo_group': repo_group.get_api_data()
128 }
83 }
129 expected = ret
84 expected = ret
85 try:
130 assert_ok(id_, expected, given=response.body)
86 assert_ok(id_, expected, given=response.body)
87 finally:
131 fixture.destroy_repo_group(full_repo_group_name)
88 fixture.destroy_repo_group(full_repo_group_name)
132 fixture.destroy_repo_group(repo_group_name)
89 fixture.destroy_repo_group(repo_group_name)
133
90
134 def test_api_create_repo_group_in_another_group_not_existing(self):
91 def test_api_create_repo_group_in_another_group_not_existing(self):
135 repo_group_name = 'api-repo-group-no'
92 repo_group_name = 'api-repo-group-no'
136
93
137 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
94 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
138 assert repo_group is None
95 assert repo_group is None
139
96
140 full_repo_group_name = repo_group_name+'/'+repo_group_name
97 full_repo_group_name = repo_group_name+'/'+repo_group_name
141 id_, params = build_data(
98 id_, params = build_data(
142 self.apikey, 'create_repo_group',
99 self.apikey, 'create_repo_group',
143 group_name=full_repo_group_name,
100 group_name=full_repo_group_name,
144 owner=TEST_USER_ADMIN_LOGIN,
101 owner=TEST_USER_ADMIN_LOGIN,
145 copy_permissions=True)
102 copy_permissions=True)
146 response = api_call(self.app, params)
103 response = api_call(self.app, params)
147 expected = 'repository group `%s` does not exist' % (repo_group_name,)
104 expected = {
105 'repo_group':
106 'Parent repository group `{}` does not exist'.format(
107 repo_group_name)}
148 assert_error(id_, expected, given=response.body)
108 assert_error(id_, expected, given=response.body)
149
109
150 def test_api_create_repo_group_that_exists(self):
110 def test_api_create_repo_group_that_exists(self):
151 repo_group_name = 'api-repo-group'
111 repo_group_name = 'api-repo-group'
152
112
153 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
113 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
154 assert repo_group is None
114 assert repo_group is None
155
115
156 fixture.create_repo_group(repo_group_name)
116 fixture.create_repo_group(repo_group_name)
157 id_, params = build_data(
117 id_, params = build_data(
158 self.apikey, 'create_repo_group',
118 self.apikey, 'create_repo_group',
159 group_name=repo_group_name,
119 group_name=repo_group_name,
160 owner=TEST_USER_ADMIN_LOGIN,)
120 owner=TEST_USER_ADMIN_LOGIN,)
161 response = api_call(self.app, params)
121 response = api_call(self.app, params)
162 expected = 'repo group `%s` already exist' % (repo_group_name,)
122 expected = {
123 'unique_repo_group_name':
124 'Repository group with name `{}` already exists'.format(
125 repo_group_name)}
126 try:
163 assert_error(id_, expected, given=response.body)
127 assert_error(id_, expected, given=response.body)
128 finally:
164 fixture.destroy_repo_group(repo_group_name)
129 fixture.destroy_repo_group(repo_group_name)
165
130
131 def test_api_create_repo_group_regular_user_wit_root_location_perms(
132 self, user_util):
133 regular_user = user_util.create_user()
134 regular_user_api_key = regular_user.api_key
135
136 repo_group_name = 'api-repo-group-by-regular-user'
137
138 usr = UserModel().get_by_username(regular_user.username)
139 usr.inherit_default_permissions = False
140 Session().add(usr)
141
142 UserModel().grant_perm(
143 regular_user.username, 'hg.repogroup.create.true')
144 Session().commit()
145
146 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
147 assert repo_group is None
148
149 id_, params = build_data(
150 regular_user_api_key, 'create_repo_group',
151 group_name=repo_group_name)
152 response = api_call(self.app, params)
153
154 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
155 assert repo_group is not None
156 expected = {
157 'msg': 'Created new repo group `%s`' % (repo_group_name,),
158 'repo_group': repo_group.get_api_data()
159 }
160 try:
161 assert_ok(id_, expected, given=response.body)
162 finally:
163 fixture.destroy_repo_group(repo_group_name)
164
165 def test_api_create_repo_group_regular_user_with_admin_perms_to_parent(
166 self, user_util):
167
168 repo_group_name = 'api-repo-group-parent'
169
170 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
171 assert repo_group is None
172 # create the parent
173 fixture.create_repo_group(repo_group_name)
174
175 # user perms
176 regular_user = user_util.create_user()
177 regular_user_api_key = regular_user.api_key
178
179 usr = UserModel().get_by_username(regular_user.username)
180 usr.inherit_default_permissions = False
181 Session().add(usr)
182
183 RepoGroupModel().grant_user_permission(
184 repo_group_name, regular_user.username, 'group.admin')
185 Session().commit()
186
187 full_repo_group_name = repo_group_name + '/' + repo_group_name
188 id_, params = build_data(
189 regular_user_api_key, 'create_repo_group',
190 group_name=full_repo_group_name)
191 response = api_call(self.app, params)
192
193 repo_group = RepoGroupModel.cls.get_by_group_name(full_repo_group_name)
194 assert repo_group is not None
195 expected = {
196 'msg': 'Created new repo group `{}`'.format(full_repo_group_name),
197 'repo_group': repo_group.get_api_data()
198 }
199 try:
200 assert_ok(id_, expected, given=response.body)
201 finally:
202 fixture.destroy_repo_group(full_repo_group_name)
203 fixture.destroy_repo_group(repo_group_name)
204
205 def test_api_create_repo_group_regular_user_no_permission_to_create_to_root_level(self):
206 repo_group_name = 'api-repo-group'
207
208 id_, params = build_data(
209 self.apikey_regular, 'create_repo_group',
210 group_name=repo_group_name)
211 response = api_call(self.app, params)
212
213 expected = {
214 'repo_group':
215 u'You do not have the permission to store '
216 u'repository groups in the root location.'}
217 assert_error(id_, expected, given=response.body)
218
219 def test_api_create_repo_group_regular_user_no_parent_group_perms(self):
220 repo_group_name = 'api-repo-group-regular-user'
221
222 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
223 assert repo_group is None
224 # create the parent
225 fixture.create_repo_group(repo_group_name)
226
227 full_repo_group_name = repo_group_name+'/'+repo_group_name
228
229 id_, params = build_data(
230 self.apikey_regular, 'create_repo_group',
231 group_name=full_repo_group_name)
232 response = api_call(self.app, params)
233
234 expected = {
235 'repo_group':
236 'Parent repository group `{}` does not exist'.format(
237 repo_group_name)}
238 try:
239 assert_error(id_, expected, given=response.body)
240 finally:
241 fixture.destroy_repo_group(repo_group_name)
242
243 def test_api_create_repo_group_regular_user_no_permission_to_specify_owner(
244 self):
245 repo_group_name = 'api-repo-group'
246
247 id_, params = build_data(
248 self.apikey_regular, 'create_repo_group',
249 group_name=repo_group_name,
250 owner=TEST_USER_ADMIN_LOGIN,)
251 response = api_call(self.app, params)
252
253 expected = "Only RhodeCode super-admin can specify `owner` param"
254 assert_error(id_, expected, given=response.body)
255
166 @mock.patch.object(RepoGroupModel, 'create', crash)
256 @mock.patch.object(RepoGroupModel, 'create', crash)
167 def test_api_create_repo_group_exception_occurred(self):
257 def test_api_create_repo_group_exception_occurred(self):
168 repo_group_name = 'api-repo-group'
258 repo_group_name = 'api-repo-group'
169
259
170 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
260 repo_group = RepoGroupModel.cls.get_by_group_name(repo_group_name)
171 assert repo_group is None
261 assert repo_group is None
172
262
173 id_, params = build_data(
263 id_, params = build_data(
174 self.apikey, 'create_repo_group',
264 self.apikey, 'create_repo_group',
175 group_name=repo_group_name,
265 group_name=repo_group_name,
176 owner=TEST_USER_ADMIN_LOGIN,)
266 owner=TEST_USER_ADMIN_LOGIN,)
177 response = api_call(self.app, params)
267 response = api_call(self.app, params)
178 expected = 'failed to create repo group `%s`' % (repo_group_name,)
268 expected = 'failed to create repo group `%s`' % (repo_group_name,)
179 assert_error(id_, expected, given=response.body)
269 assert_error(id_, expected, given=response.body)
180
270
181 def test_create_group_with_extra_slashes_in_name(self, user_util):
271 def test_create_group_with_extra_slashes_in_name(self, user_util):
182 existing_repo_group = user_util.create_repo_group()
272 existing_repo_group = user_util.create_repo_group()
183 dirty_group_name = '//{}//group2//'.format(
273 dirty_group_name = '//{}//group2//'.format(
184 existing_repo_group.group_name)
274 existing_repo_group.group_name)
185 cleaned_group_name = '{}/group2'.format(
275 cleaned_group_name = '{}/group2'.format(
186 existing_repo_group.group_name)
276 existing_repo_group.group_name)
187
277
188 id_, params = build_data(
278 id_, params = build_data(
189 self.apikey, 'create_repo_group',
279 self.apikey, 'create_repo_group',
190 group_name=dirty_group_name,
280 group_name=dirty_group_name,
191 owner=TEST_USER_ADMIN_LOGIN,)
281 owner=TEST_USER_ADMIN_LOGIN,)
192 response = api_call(self.app, params)
282 response = api_call(self.app, params)
193 repo_group = RepoGroupModel.cls.get_by_group_name(cleaned_group_name)
283 repo_group = RepoGroupModel.cls.get_by_group_name(cleaned_group_name)
194 expected = {
284 expected = {
195 'msg': 'Created new repo group `%s`' % (cleaned_group_name,),
285 'msg': 'Created new repo group `%s`' % (cleaned_group_name,),
196 'repo_group': repo_group.get_api_data()
286 'repo_group': repo_group.get_api_data()
197 }
287 }
198 assert_ok(id_, expected, given=response.body)
288 assert_ok(id_, expected, given=response.body)
199 fixture.destroy_repo_group(cleaned_group_name)
289 fixture.destroy_repo_group(cleaned_group_name)
@@ -1,107 +1,150 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 import os
21 import os
22
22
23 import pytest
23 import pytest
24
24
25 from rhodecode.model.repo_group import RepoGroupModel
25 from rhodecode.model.repo_group import RepoGroupModel
26 from rhodecode.model.user import UserModel
26 from rhodecode.model.user import UserModel
27 from rhodecode.api.tests.utils import (
27 from rhodecode.api.tests.utils import (
28 build_data, api_call, assert_error, assert_ok)
28 build_data, api_call, assert_error, assert_ok)
29
29
30
30
31 @pytest.mark.usefixtures("testuser_api", "app")
31 @pytest.mark.usefixtures("testuser_api", "app")
32 class TestApiUpdateRepoGroup(object):
32 class TestApiUpdateRepoGroup(object):
33
33 def test_update_group_name(self, user_util):
34 def test_update_group_name(self, user_util):
34 new_group_name = 'new-group'
35 new_group_name = 'new-group'
35 initial_name = self._update(user_util, group_name=new_group_name)
36 initial_name = self._update(user_util, group_name=new_group_name)
36 assert RepoGroupModel()._get_repo_group(initial_name) is None
37 assert RepoGroupModel()._get_repo_group(initial_name) is None
37 assert RepoGroupModel()._get_repo_group(new_group_name) is not None
38 new_group = RepoGroupModel()._get_repo_group(new_group_name)
39 assert new_group is not None
40 assert new_group.full_path == new_group_name
38
41
39 def test_update_parent(self, user_util):
42 def test_update_group_name_change_parent(self, user_util):
43
40 parent_group = user_util.create_repo_group()
44 parent_group = user_util.create_repo_group()
41 initial_name = self._update(user_util, parent=parent_group.name)
45 parent_group_name = parent_group.name
42
46
43 expected_group_name = '{}/{}'.format(parent_group.name, initial_name)
47 expected_group_name = '{}/{}'.format(parent_group_name, 'new-group')
48 initial_name = self._update(user_util, group_name=expected_group_name)
49
44 repo_group = RepoGroupModel()._get_repo_group(expected_group_name)
50 repo_group = RepoGroupModel()._get_repo_group(expected_group_name)
51
45 assert repo_group is not None
52 assert repo_group is not None
46 assert repo_group.group_name == expected_group_name
53 assert repo_group.group_name == expected_group_name
47 assert repo_group.name == initial_name
54 assert repo_group.full_path == expected_group_name
48 assert RepoGroupModel()._get_repo_group(initial_name) is None
55 assert RepoGroupModel()._get_repo_group(initial_name) is None
56
49 new_path = os.path.join(
57 new_path = os.path.join(
50 RepoGroupModel().repos_path, *repo_group.full_path_splitted)
58 RepoGroupModel().repos_path, *repo_group.full_path_splitted)
51 assert os.path.exists(new_path)
59 assert os.path.exists(new_path)
52
60
53 def test_update_enable_locking(self, user_util):
61 def test_update_enable_locking(self, user_util):
54 initial_name = self._update(user_util, enable_locking=True)
62 initial_name = self._update(user_util, enable_locking=True)
55 repo_group = RepoGroupModel()._get_repo_group(initial_name)
63 repo_group = RepoGroupModel()._get_repo_group(initial_name)
56 assert repo_group.enable_locking is True
64 assert repo_group.enable_locking is True
57
65
58 def test_update_description(self, user_util):
66 def test_update_description(self, user_util):
59 description = 'New description'
67 description = 'New description'
60 initial_name = self._update(user_util, description=description)
68 initial_name = self._update(user_util, description=description)
61 repo_group = RepoGroupModel()._get_repo_group(initial_name)
69 repo_group = RepoGroupModel()._get_repo_group(initial_name)
62 assert repo_group.group_description == description
70 assert repo_group.group_description == description
63
71
64 def test_update_owner(self, user_util):
72 def test_update_owner(self, user_util):
65 owner = self.TEST_USER_LOGIN
73 owner = self.TEST_USER_LOGIN
66 initial_name = self._update(user_util, owner=owner)
74 initial_name = self._update(user_util, owner=owner)
67 repo_group = RepoGroupModel()._get_repo_group(initial_name)
75 repo_group = RepoGroupModel()._get_repo_group(initial_name)
68 assert repo_group.user.username == owner
76 assert repo_group.user.username == owner
69
77
70 def test_api_update_repo_group_by_regular_user_no_permission(
78 def test_update_group_name_conflict_with_existing(self, user_util):
71 self, backend):
79 group_1 = user_util.create_repo_group()
72 repo = backend.create_repo()
80 group_2 = user_util.create_repo_group()
73 repo_name = repo.repo_name
81 repo_group_name_1 = group_1.group_name
82 repo_group_name_2 = group_2.group_name
74
83
75 id_, params = build_data(
84 id_, params = build_data(
76 self.apikey_regular, 'update_repo_group', repogroupid=repo_name)
85 self.apikey, 'update_repo_group', repogroupid=repo_group_name_1,
86 group_name=repo_group_name_2)
87 response = api_call(self.app, params)
88 expected = {
89 'unique_repo_group_name':
90 'Repository group with name `{}` already exists'.format(
91 repo_group_name_2)}
92 assert_error(id_, expected, given=response.body)
93
94 def test_api_update_repo_group_by_regular_user_no_permission(self, user_util):
95 temp_user = user_util.create_user()
96 temp_user_api_key = temp_user.api_key
97 parent_group = user_util.create_repo_group()
98 repo_group_name = parent_group.group_name
99 id_, params = build_data(
100 temp_user_api_key, 'update_repo_group', repogroupid=repo_group_name)
77 response = api_call(self.app, params)
101 response = api_call(self.app, params)
78 expected = 'repository group `%s` does not exist' % (repo_name,)
102 expected = 'repository group `%s` does not exist' % (repo_group_name,)
103 assert_error(id_, expected, given=response.body)
104
105 def test_api_update_repo_group_regular_user_no_root_write_permissions(
106 self, user_util):
107 temp_user = user_util.create_user()
108 temp_user_api_key = temp_user.api_key
109 parent_group = user_util.create_repo_group(owner=temp_user.username)
110 repo_group_name = parent_group.group_name
111
112 id_, params = build_data(
113 temp_user_api_key, 'update_repo_group', repogroupid=repo_group_name,
114 group_name='at-root-level')
115 response = api_call(self.app, params)
116 expected = {
117 'repo_group': 'You do not have the permission to store '
118 'repository groups in the root location.'}
79 assert_error(id_, expected, given=response.body)
119 assert_error(id_, expected, given=response.body)
80
120
81 def _update(self, user_util, **kwargs):
121 def _update(self, user_util, **kwargs):
82 repo_group = user_util.create_repo_group()
122 repo_group = user_util.create_repo_group()
83 initial_name = repo_group.name
123 initial_name = repo_group.name
84 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
124 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
85 user_util.grant_user_permission_to_repo_group(
125 user_util.grant_user_permission_to_repo_group(
86 repo_group, user, 'group.admin')
126 repo_group, user, 'group.admin')
87
127
88 id_, params = build_data(
128 id_, params = build_data(
89 self.apikey, 'update_repo_group', repogroupid=initial_name,
129 self.apikey, 'update_repo_group', repogroupid=initial_name,
90 **kwargs)
130 **kwargs)
91 response = api_call(self.app, params)
131 response = api_call(self.app, params)
92 ret = {
132
133 repo_group = RepoGroupModel.cls.get(repo_group.group_id)
134
135 expected = {
93 'msg': 'updated repository group ID:{} {}'.format(
136 'msg': 'updated repository group ID:{} {}'.format(
94 repo_group.group_id, repo_group.group_name),
137 repo_group.group_id, repo_group.group_name),
95 'repo_group': {
138 'repo_group': {
96 'repositories': [],
139 'repositories': [],
97 'group_name': repo_group.group_name,
140 'group_name': repo_group.group_name,
98 'group_description': repo_group.group_description,
141 'group_description': repo_group.group_description,
99 'owner': repo_group.user.username,
142 'owner': repo_group.user.username,
100 'group_id': repo_group.group_id,
143 'group_id': repo_group.group_id,
101 'parent_group': (
144 'parent_group': (
102 repo_group.parent_group.name
145 repo_group.parent_group.name
103 if repo_group.parent_group else None)
146 if repo_group.parent_group else None)
104 }
147 }
105 }
148 }
106 assert_ok(id_, ret, given=response.body)
149 assert_ok(id_, expected, given=response.body)
107 return initial_name
150 return initial_name
@@ -1,699 +1,702 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-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 import logging
22 import logging
23
23
24 import colander
24 from rhodecode.api import JSONRPCValidationError
25
25 from rhodecode.api import jsonrpc_method, JSONRPCError
26 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
27 from rhodecode.api.utils import (
26 from rhodecode.api.utils import (
28 has_superadmin_permission, Optional, OAttr, get_user_or_error,
27 has_superadmin_permission, Optional, OAttr, get_user_or_error,
29 store_update, get_repo_group_or_error,
28 get_repo_group_or_error, get_perm_or_error, get_user_group_or_error,
30 get_perm_or_error, get_user_group_or_error, get_origin)
29 get_origin, validate_repo_group_permissions, validate_set_owner_permissions)
31 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
32 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
31 HasRepoGroupPermissionAnyApi, HasUserGroupPermissionAnyApi)
33 HasUserGroupPermissionAnyApi)
32 from rhodecode.model.db import Session
34 from rhodecode.model.db import Session, RepoGroup
35 from rhodecode.model.repo_group import RepoGroupModel
33 from rhodecode.model.repo_group import RepoGroupModel
36 from rhodecode.model.scm import RepoGroupList
34 from rhodecode.model.scm import RepoGroupList
35 from rhodecode.model import validation_schema
37 from rhodecode.model.validation_schema.schemas import repo_group_schema
36 from rhodecode.model.validation_schema.schemas import repo_group_schema
38
37
39
38
40 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
41
40
42
41
43 @jsonrpc_method()
42 @jsonrpc_method()
44 def get_repo_group(request, apiuser, repogroupid):
43 def get_repo_group(request, apiuser, repogroupid):
45 """
44 """
46 Return the specified |repo| group, along with permissions,
45 Return the specified |repo| group, along with permissions,
47 and repositories inside the group
46 and repositories inside the group
48
47
49 :param apiuser: This is filled automatically from the |authtoken|.
48 :param apiuser: This is filled automatically from the |authtoken|.
50 :type apiuser: AuthUser
49 :type apiuser: AuthUser
51 :param repogroupid: Specify the name of ID of the repository group.
50 :param repogroupid: Specify the name of ID of the repository group.
52 :type repogroupid: str or int
51 :type repogroupid: str or int
53
52
54
53
55 Example output:
54 Example output:
56
55
57 .. code-block:: bash
56 .. code-block:: bash
58
57
59 {
58 {
60 "error": null,
59 "error": null,
61 "id": repo-group-id,
60 "id": repo-group-id,
62 "result": {
61 "result": {
63 "group_description": "repo group description",
62 "group_description": "repo group description",
64 "group_id": 14,
63 "group_id": 14,
65 "group_name": "group name",
64 "group_name": "group name",
66 "members": [
65 "members": [
67 {
66 {
68 "name": "super-admin-username",
67 "name": "super-admin-username",
69 "origin": "super-admin",
68 "origin": "super-admin",
70 "permission": "group.admin",
69 "permission": "group.admin",
71 "type": "user"
70 "type": "user"
72 },
71 },
73 {
72 {
74 "name": "owner-name",
73 "name": "owner-name",
75 "origin": "owner",
74 "origin": "owner",
76 "permission": "group.admin",
75 "permission": "group.admin",
77 "type": "user"
76 "type": "user"
78 },
77 },
79 {
78 {
80 "name": "user-group-name",
79 "name": "user-group-name",
81 "origin": "permission",
80 "origin": "permission",
82 "permission": "group.write",
81 "permission": "group.write",
83 "type": "user_group"
82 "type": "user_group"
84 }
83 }
85 ],
84 ],
86 "owner": "owner-name",
85 "owner": "owner-name",
87 "parent_group": null,
86 "parent_group": null,
88 "repositories": [ repo-list ]
87 "repositories": [ repo-list ]
89 }
88 }
90 }
89 }
91 """
90 """
92
91
93 repo_group = get_repo_group_or_error(repogroupid)
92 repo_group = get_repo_group_or_error(repogroupid)
94 if not has_superadmin_permission(apiuser):
93 if not has_superadmin_permission(apiuser):
95 # check if we have at least read permission for this repo group !
94 # check if we have at least read permission for this repo group !
96 _perms = ('group.admin', 'group.write', 'group.read',)
95 _perms = ('group.admin', 'group.write', 'group.read',)
97 if not HasRepoGroupPermissionAnyApi(*_perms)(
96 if not HasRepoGroupPermissionAnyApi(*_perms)(
98 user=apiuser, group_name=repo_group.group_name):
97 user=apiuser, group_name=repo_group.group_name):
99 raise JSONRPCError(
98 raise JSONRPCError(
100 'repository group `%s` does not exist' % (repogroupid,))
99 'repository group `%s` does not exist' % (repogroupid,))
101
100
102 permissions = []
101 permissions = []
103 for _user in repo_group.permissions():
102 for _user in repo_group.permissions():
104 user_data = {
103 user_data = {
105 'name': _user.username,
104 'name': _user.username,
106 'permission': _user.permission,
105 'permission': _user.permission,
107 'origin': get_origin(_user),
106 'origin': get_origin(_user),
108 'type': "user",
107 'type': "user",
109 }
108 }
110 permissions.append(user_data)
109 permissions.append(user_data)
111
110
112 for _user_group in repo_group.permission_user_groups():
111 for _user_group in repo_group.permission_user_groups():
113 user_group_data = {
112 user_group_data = {
114 'name': _user_group.users_group_name,
113 'name': _user_group.users_group_name,
115 'permission': _user_group.permission,
114 'permission': _user_group.permission,
116 'origin': get_origin(_user_group),
115 'origin': get_origin(_user_group),
117 'type': "user_group",
116 'type': "user_group",
118 }
117 }
119 permissions.append(user_group_data)
118 permissions.append(user_group_data)
120
119
121 data = repo_group.get_api_data()
120 data = repo_group.get_api_data()
122 data["members"] = permissions # TODO: this should be named permissions
121 data["members"] = permissions # TODO: this should be named permissions
123 return data
122 return data
124
123
125
124
126 @jsonrpc_method()
125 @jsonrpc_method()
127 def get_repo_groups(request, apiuser):
126 def get_repo_groups(request, apiuser):
128 """
127 """
129 Returns all repository groups.
128 Returns all repository groups.
130
129
131 :param apiuser: This is filled automatically from the |authtoken|.
130 :param apiuser: This is filled automatically from the |authtoken|.
132 :type apiuser: AuthUser
131 :type apiuser: AuthUser
133 """
132 """
134
133
135 result = []
134 result = []
136 _perms = ('group.read', 'group.write', 'group.admin',)
135 _perms = ('group.read', 'group.write', 'group.admin',)
137 extras = {'user': apiuser}
136 extras = {'user': apiuser}
138 for repo_group in RepoGroupList(RepoGroupModel().get_all(),
137 for repo_group in RepoGroupList(RepoGroupModel().get_all(),
139 perm_set=_perms, extra_kwargs=extras):
138 perm_set=_perms, extra_kwargs=extras):
140 result.append(repo_group.get_api_data())
139 result.append(repo_group.get_api_data())
141 return result
140 return result
142
141
143
142
144 @jsonrpc_method()
143 @jsonrpc_method()
145 def create_repo_group(request, apiuser, group_name, description=Optional(''),
144 def create_repo_group(
145 request, apiuser, group_name,
146 owner=Optional(OAttr('apiuser')),
146 owner=Optional(OAttr('apiuser')),
147 description=Optional(''),
147 copy_permissions=Optional(False)):
148 copy_permissions=Optional(False)):
148 """
149 """
149 Creates a repository group.
150 Creates a repository group.
150
151
151 * If the repository group name contains "/", all the required repository
152 * If the repository group name contains "/", repository group will be
152 groups will be created.
153 created inside a repository group or nested repository groups
153
154
154 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
155 For example "foo/bar/group1" will create repository group called "group1"
155 (with "foo" as parent). It will also create the "baz" repository
156 inside group "foo/bar". You have to have permissions to access and
156 with "bar" as |repo| group.
157 write to the last repository group ("bar" in this example)
157
158
158 This command can only be run using an |authtoken| with admin
159 This command can only be run using an |authtoken| with at least
159 permissions.
160 permissions to create repository groups, or admin permissions to
161 parent repository groups.
160
162
161 :param apiuser: This is filled automatically from the |authtoken|.
163 :param apiuser: This is filled automatically from the |authtoken|.
162 :type apiuser: AuthUser
164 :type apiuser: AuthUser
163 :param group_name: Set the repository group name.
165 :param group_name: Set the repository group name.
164 :type group_name: str
166 :type group_name: str
165 :param description: Set the |repo| group description.
167 :param description: Set the |repo| group description.
166 :type description: str
168 :type description: str
167 :param owner: Set the |repo| group owner.
169 :param owner: Set the |repo| group owner.
168 :type owner: str
170 :type owner: str
169 :param copy_permissions:
171 :param copy_permissions:
170 :type copy_permissions:
172 :type copy_permissions:
171
173
172 Example output:
174 Example output:
173
175
174 .. code-block:: bash
176 .. code-block:: bash
175
177
176 id : <id_given_in_input>
178 id : <id_given_in_input>
177 result : {
179 result : {
178 "msg": "Created new repo group `<repo_group_name>`"
180 "msg": "Created new repo group `<repo_group_name>`"
179 "repo_group": <repogroup_object>
181 "repo_group": <repogroup_object>
180 }
182 }
181 error : null
183 error : null
182
184
183
185
184 Example error output:
186 Example error output:
185
187
186 .. code-block:: bash
188 .. code-block:: bash
187
189
188 id : <id_given_in_input>
190 id : <id_given_in_input>
189 result : null
191 result : null
190 error : {
192 error : {
191 failed to create repo group `<repogroupid>`
193 failed to create repo group `<repogroupid>`
192 }
194 }
193
195
194 """
196 """
195
197
196 schema = repo_group_schema.RepoGroupSchema()
198 owner = validate_set_owner_permissions(apiuser, owner)
197 try:
198 data = schema.deserialize({
199 'group_name': group_name
200 })
201 except colander.Invalid as e:
202 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
203 group_name = data['group_name']
204
199
205 if isinstance(owner, Optional):
200 description = Optional.extract(description)
206 owner = apiuser.user_id
207
208 group_description = Optional.extract(description)
209 copy_permissions = Optional.extract(copy_permissions)
201 copy_permissions = Optional.extract(copy_permissions)
210
202
211 # get by full name with parents, check if it already exist
203 schema = repo_group_schema.RepoGroupSchema().bind(
212 if RepoGroup.get_by_group_name(group_name):
204 # user caller
213 raise JSONRPCError("repo group `%s` already exist" % (group_name,))
205 user=apiuser)
214
215 (group_name_cleaned,
216 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
217 group_name)
218
206
219 parent_group = None
207 try:
220 if parent_group_name:
208 schema_data = schema.deserialize(dict(
221 parent_group = get_repo_group_or_error(parent_group_name)
209 repo_group_name=group_name,
210 repo_group_owner=owner.username,
211 repo_group_description=description,
212 repo_group_copy_permissions=copy_permissions,
213 ))
214 except validation_schema.Invalid as err:
215 raise JSONRPCValidationError(colander_exc=err)
222
216
223 if not HasPermissionAnyApi(
217 validated_group_name = schema_data['repo_group_name']
224 'hg.admin', 'hg.repogroup.create.true')(user=apiuser):
225 # check if we have admin permission for this parent repo group !
226 # users without admin or hg.repogroup.create can only create other
227 # groups in groups they own so this is a required, but can be empty
228 parent_group = getattr(parent_group, 'group_name', '')
229 _perms = ('group.admin',)
230 if not HasRepoGroupPermissionAnyApi(*_perms)(
231 user=apiuser, group_name=parent_group):
232 raise JSONRPCForbidden()
233
218
234 try:
219 try:
235 repo_group = RepoGroupModel().create(
220 repo_group = RepoGroupModel().create(
236 group_name=group_name,
237 group_description=group_description,
238 owner=owner,
221 owner=owner,
239 copy_permissions=copy_permissions)
222 group_name=validated_group_name,
223 group_description=schema_data['repo_group_name'],
224 copy_permissions=schema_data['repo_group_copy_permissions'])
240 Session().commit()
225 Session().commit()
241 return {
226 return {
242 'msg': 'Created new repo group `%s`' % group_name,
227 'msg': 'Created new repo group `%s`' % validated_group_name,
243 'repo_group': repo_group.get_api_data()
228 'repo_group': repo_group.get_api_data()
244 }
229 }
245 except Exception:
230 except Exception:
246 log.exception("Exception occurred while trying create repo group")
231 log.exception("Exception occurred while trying create repo group")
247 raise JSONRPCError(
232 raise JSONRPCError(
248 'failed to create repo group `%s`' % (group_name,))
233 'failed to create repo group `%s`' % (validated_group_name,))
249
234
250
235
251 @jsonrpc_method()
236 @jsonrpc_method()
252 def update_repo_group(
237 def update_repo_group(
253 request, apiuser, repogroupid, group_name=Optional(''),
238 request, apiuser, repogroupid, group_name=Optional(''),
254 description=Optional(''), owner=Optional(OAttr('apiuser')),
239 description=Optional(''), owner=Optional(OAttr('apiuser')),
255 parent=Optional(None), enable_locking=Optional(False)):
240 enable_locking=Optional(False)):
256 """
241 """
257 Updates repository group with the details given.
242 Updates repository group with the details given.
258
243
259 This command can only be run using an |authtoken| with admin
244 This command can only be run using an |authtoken| with admin
260 permissions.
245 permissions.
261
246
247 * If the group_name name contains "/", repository group will be updated
248 accordingly with a repository group or nested repository groups
249
250 For example repogroupid=group-test group_name="foo/bar/group-test"
251 will update repository group called "group-test" and place it
252 inside group "foo/bar".
253 You have to have permissions to access and write to the last repository
254 group ("bar" in this example)
255
262 :param apiuser: This is filled automatically from the |authtoken|.
256 :param apiuser: This is filled automatically from the |authtoken|.
263 :type apiuser: AuthUser
257 :type apiuser: AuthUser
264 :param repogroupid: Set the ID of repository group.
258 :param repogroupid: Set the ID of repository group.
265 :type repogroupid: str or int
259 :type repogroupid: str or int
266 :param group_name: Set the name of the |repo| group.
260 :param group_name: Set the name of the |repo| group.
267 :type group_name: str
261 :type group_name: str
268 :param description: Set a description for the group.
262 :param description: Set a description for the group.
269 :type description: str
263 :type description: str
270 :param owner: Set the |repo| group owner.
264 :param owner: Set the |repo| group owner.
271 :type owner: str
265 :type owner: str
272 :param parent: Set the |repo| group parent.
273 :type parent: str or int
274 :param enable_locking: Enable |repo| locking. The default is false.
266 :param enable_locking: Enable |repo| locking. The default is false.
275 :type enable_locking: bool
267 :type enable_locking: bool
276 """
268 """
277
269
278 repo_group = get_repo_group_or_error(repogroupid)
270 repo_group = get_repo_group_or_error(repogroupid)
271
279 if not has_superadmin_permission(apiuser):
272 if not has_superadmin_permission(apiuser):
280 # check if we have admin permission for this repo group !
273 validate_repo_group_permissions(
281 _perms = ('group.admin',)
274 apiuser, repogroupid, repo_group, ('group.admin',))
282 if not HasRepoGroupPermissionAnyApi(*_perms)(
275
283 user=apiuser, group_name=repo_group.group_name):
276 updates = dict(
284 raise JSONRPCError(
277 group_name=group_name
285 'repository group `%s` does not exist' % (repogroupid,))
278 if not isinstance(group_name, Optional) else repo_group.group_name,
279
280 group_description=description
281 if not isinstance(description, Optional) else repo_group.group_description,
282
283 user=owner
284 if not isinstance(owner, Optional) else repo_group.user.username,
285
286 enable_locking=enable_locking
287 if not isinstance(enable_locking, Optional) else repo_group.enable_locking
288 )
286
289
287 updates = {}
290 schema = repo_group_schema.RepoGroupSchema().bind(
291 # user caller
292 user=apiuser,
293 old_values=repo_group.get_api_data())
294
288 try:
295 try:
289 store_update(updates, group_name, 'group_name')
296 schema_data = schema.deserialize(dict(
290 store_update(updates, description, 'group_description')
297 repo_group_name=updates['group_name'],
291 store_update(updates, owner, 'user')
298 repo_group_owner=updates['user'],
292 store_update(updates, parent, 'group_parent_id')
299 repo_group_description=updates['group_description'],
293 store_update(updates, enable_locking, 'enable_locking')
300 repo_group_enable_locking=updates['enable_locking'],
294 repo_group = RepoGroupModel().update(repo_group, updates)
301 ))
302 except validation_schema.Invalid as err:
303 raise JSONRPCValidationError(colander_exc=err)
304
305 validated_updates = dict(
306 group_name=schema_data['repo_group']['repo_group_name_without_group'],
307 group_parent_id=schema_data['repo_group']['repo_group_id'],
308 user=schema_data['repo_group_owner'],
309 group_description=schema_data['repo_group_description'],
310 enable_locking=schema_data['repo_group_enable_locking'],
311 )
312
313 try:
314 RepoGroupModel().update(repo_group, validated_updates)
295 Session().commit()
315 Session().commit()
296 return {
316 return {
297 'msg': 'updated repository group ID:%s %s' % (
317 'msg': 'updated repository group ID:%s %s' % (
298 repo_group.group_id, repo_group.group_name),
318 repo_group.group_id, repo_group.group_name),
299 'repo_group': repo_group.get_api_data()
319 'repo_group': repo_group.get_api_data()
300 }
320 }
301 except Exception:
321 except Exception:
302 log.exception("Exception occurred while trying update repo group")
322 log.exception(
323 u"Exception occurred while trying update repo group %s",
324 repogroupid)
303 raise JSONRPCError('failed to update repository group `%s`'
325 raise JSONRPCError('failed to update repository group `%s`'
304 % (repogroupid,))
326 % (repogroupid,))
305
327
306
328
307 @jsonrpc_method()
329 @jsonrpc_method()
308 def delete_repo_group(request, apiuser, repogroupid):
330 def delete_repo_group(request, apiuser, repogroupid):
309 """
331 """
310 Deletes a |repo| group.
332 Deletes a |repo| group.
311
333
312 :param apiuser: This is filled automatically from the |authtoken|.
334 :param apiuser: This is filled automatically from the |authtoken|.
313 :type apiuser: AuthUser
335 :type apiuser: AuthUser
314 :param repogroupid: Set the name or ID of repository group to be
336 :param repogroupid: Set the name or ID of repository group to be
315 deleted.
337 deleted.
316 :type repogroupid: str or int
338 :type repogroupid: str or int
317
339
318 Example output:
340 Example output:
319
341
320 .. code-block:: bash
342 .. code-block:: bash
321
343
322 id : <id_given_in_input>
344 id : <id_given_in_input>
323 result : {
345 result : {
324 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>'
346 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>'
325 'repo_group': null
347 'repo_group': null
326 }
348 }
327 error : null
349 error : null
328
350
329 Example error output:
351 Example error output:
330
352
331 .. code-block:: bash
353 .. code-block:: bash
332
354
333 id : <id_given_in_input>
355 id : <id_given_in_input>
334 result : null
356 result : null
335 error : {
357 error : {
336 "failed to delete repo group ID:<repogroupid> <repogroupname>"
358 "failed to delete repo group ID:<repogroupid> <repogroupname>"
337 }
359 }
338
360
339 """
361 """
340
362
341 repo_group = get_repo_group_or_error(repogroupid)
363 repo_group = get_repo_group_or_error(repogroupid)
342 if not has_superadmin_permission(apiuser):
364 if not has_superadmin_permission(apiuser):
343 # check if we have admin permission for this repo group !
365 validate_repo_group_permissions(
344 _perms = ('group.admin',)
366 apiuser, repogroupid, repo_group, ('group.admin',))
345 if not HasRepoGroupPermissionAnyApi(*_perms)(
367
346 user=apiuser, group_name=repo_group.group_name):
347 raise JSONRPCError(
348 'repository group `%s` does not exist' % (repogroupid,))
349 try:
368 try:
350 RepoGroupModel().delete(repo_group)
369 RepoGroupModel().delete(repo_group)
351 Session().commit()
370 Session().commit()
352 return {
371 return {
353 'msg': 'deleted repo group ID:%s %s' %
372 'msg': 'deleted repo group ID:%s %s' %
354 (repo_group.group_id, repo_group.group_name),
373 (repo_group.group_id, repo_group.group_name),
355 'repo_group': None
374 'repo_group': None
356 }
375 }
357 except Exception:
376 except Exception:
358 log.exception("Exception occurred while trying to delete repo group")
377 log.exception("Exception occurred while trying to delete repo group")
359 raise JSONRPCError('failed to delete repo group ID:%s %s' %
378 raise JSONRPCError('failed to delete repo group ID:%s %s' %
360 (repo_group.group_id, repo_group.group_name))
379 (repo_group.group_id, repo_group.group_name))
361
380
362
381
363 @jsonrpc_method()
382 @jsonrpc_method()
364 def grant_user_permission_to_repo_group(
383 def grant_user_permission_to_repo_group(
365 request, apiuser, repogroupid, userid, perm,
384 request, apiuser, repogroupid, userid, perm,
366 apply_to_children=Optional('none')):
385 apply_to_children=Optional('none')):
367 """
386 """
368 Grant permission for a user on the given repository group, or update
387 Grant permission for a user on the given repository group, or update
369 existing permissions if found.
388 existing permissions if found.
370
389
371 This command can only be run using an |authtoken| with admin
390 This command can only be run using an |authtoken| with admin
372 permissions.
391 permissions.
373
392
374 :param apiuser: This is filled automatically from the |authtoken|.
393 :param apiuser: This is filled automatically from the |authtoken|.
375 :type apiuser: AuthUser
394 :type apiuser: AuthUser
376 :param repogroupid: Set the name or ID of repository group.
395 :param repogroupid: Set the name or ID of repository group.
377 :type repogroupid: str or int
396 :type repogroupid: str or int
378 :param userid: Set the user name.
397 :param userid: Set the user name.
379 :type userid: str
398 :type userid: str
380 :param perm: (group.(none|read|write|admin))
399 :param perm: (group.(none|read|write|admin))
381 :type perm: str
400 :type perm: str
382 :param apply_to_children: 'none', 'repos', 'groups', 'all'
401 :param apply_to_children: 'none', 'repos', 'groups', 'all'
383 :type apply_to_children: str
402 :type apply_to_children: str
384
403
385 Example output:
404 Example output:
386
405
387 .. code-block:: bash
406 .. code-block:: bash
388
407
389 id : <id_given_in_input>
408 id : <id_given_in_input>
390 result: {
409 result: {
391 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
410 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
392 "success": true
411 "success": true
393 }
412 }
394 error: null
413 error: null
395
414
396 Example error output:
415 Example error output:
397
416
398 .. code-block:: bash
417 .. code-block:: bash
399
418
400 id : <id_given_in_input>
419 id : <id_given_in_input>
401 result : null
420 result : null
402 error : {
421 error : {
403 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
422 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
404 }
423 }
405
424
406 """
425 """
407
426
408 repo_group = get_repo_group_or_error(repogroupid)
427 repo_group = get_repo_group_or_error(repogroupid)
409
428
410 if not has_superadmin_permission(apiuser):
429 if not has_superadmin_permission(apiuser):
411 # check if we have admin permission for this repo group !
430 validate_repo_group_permissions(
412 _perms = ('group.admin',)
431 apiuser, repogroupid, repo_group, ('group.admin',))
413 if not HasRepoGroupPermissionAnyApi(*_perms)(
414 user=apiuser, group_name=repo_group.group_name):
415 raise JSONRPCError(
416 'repository group `%s` does not exist' % (repogroupid,))
417
432
418 user = get_user_or_error(userid)
433 user = get_user_or_error(userid)
419 perm = get_perm_or_error(perm, prefix='group.')
434 perm = get_perm_or_error(perm, prefix='group.')
420 apply_to_children = Optional.extract(apply_to_children)
435 apply_to_children = Optional.extract(apply_to_children)
421
436
422 perm_additions = [[user.user_id, perm, "user"]]
437 perm_additions = [[user.user_id, perm, "user"]]
423 try:
438 try:
424 RepoGroupModel().update_permissions(repo_group=repo_group,
439 RepoGroupModel().update_permissions(repo_group=repo_group,
425 perm_additions=perm_additions,
440 perm_additions=perm_additions,
426 recursive=apply_to_children,
441 recursive=apply_to_children,
427 cur_user=apiuser)
442 cur_user=apiuser)
428 Session().commit()
443 Session().commit()
429 return {
444 return {
430 'msg': 'Granted perm: `%s` (recursive:%s) for user: '
445 'msg': 'Granted perm: `%s` (recursive:%s) for user: '
431 '`%s` in repo group: `%s`' % (
446 '`%s` in repo group: `%s`' % (
432 perm.permission_name, apply_to_children, user.username,
447 perm.permission_name, apply_to_children, user.username,
433 repo_group.name
448 repo_group.name
434 ),
449 ),
435 'success': True
450 'success': True
436 }
451 }
437 except Exception:
452 except Exception:
438 log.exception("Exception occurred while trying to grant "
453 log.exception("Exception occurred while trying to grant "
439 "user permissions to repo group")
454 "user permissions to repo group")
440 raise JSONRPCError(
455 raise JSONRPCError(
441 'failed to edit permission for user: '
456 'failed to edit permission for user: '
442 '`%s` in repo group: `%s`' % (userid, repo_group.name))
457 '`%s` in repo group: `%s`' % (userid, repo_group.name))
443
458
444
459
445 @jsonrpc_method()
460 @jsonrpc_method()
446 def revoke_user_permission_from_repo_group(
461 def revoke_user_permission_from_repo_group(
447 request, apiuser, repogroupid, userid,
462 request, apiuser, repogroupid, userid,
448 apply_to_children=Optional('none')):
463 apply_to_children=Optional('none')):
449 """
464 """
450 Revoke permission for a user in a given repository group.
465 Revoke permission for a user in a given repository group.
451
466
452 This command can only be run using an |authtoken| with admin
467 This command can only be run using an |authtoken| with admin
453 permissions on the |repo| group.
468 permissions on the |repo| group.
454
469
455 :param apiuser: This is filled automatically from the |authtoken|.
470 :param apiuser: This is filled automatically from the |authtoken|.
456 :type apiuser: AuthUser
471 :type apiuser: AuthUser
457 :param repogroupid: Set the name or ID of the repository group.
472 :param repogroupid: Set the name or ID of the repository group.
458 :type repogroupid: str or int
473 :type repogroupid: str or int
459 :param userid: Set the user name to revoke.
474 :param userid: Set the user name to revoke.
460 :type userid: str
475 :type userid: str
461 :param apply_to_children: 'none', 'repos', 'groups', 'all'
476 :param apply_to_children: 'none', 'repos', 'groups', 'all'
462 :type apply_to_children: str
477 :type apply_to_children: str
463
478
464 Example output:
479 Example output:
465
480
466 .. code-block:: bash
481 .. code-block:: bash
467
482
468 id : <id_given_in_input>
483 id : <id_given_in_input>
469 result: {
484 result: {
470 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
485 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
471 "success": true
486 "success": true
472 }
487 }
473 error: null
488 error: null
474
489
475 Example error output:
490 Example error output:
476
491
477 .. code-block:: bash
492 .. code-block:: bash
478
493
479 id : <id_given_in_input>
494 id : <id_given_in_input>
480 result : null
495 result : null
481 error : {
496 error : {
482 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
497 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
483 }
498 }
484
499
485 """
500 """
486
501
487 repo_group = get_repo_group_or_error(repogroupid)
502 repo_group = get_repo_group_or_error(repogroupid)
488
503
489 if not has_superadmin_permission(apiuser):
504 if not has_superadmin_permission(apiuser):
490 # check if we have admin permission for this repo group !
505 validate_repo_group_permissions(
491 _perms = ('group.admin',)
506 apiuser, repogroupid, repo_group, ('group.admin',))
492 if not HasRepoGroupPermissionAnyApi(*_perms)(
493 user=apiuser, group_name=repo_group.group_name):
494 raise JSONRPCError(
495 'repository group `%s` does not exist' % (repogroupid,))
496
507
497 user = get_user_or_error(userid)
508 user = get_user_or_error(userid)
498 apply_to_children = Optional.extract(apply_to_children)
509 apply_to_children = Optional.extract(apply_to_children)
499
510
500 perm_deletions = [[user.user_id, None, "user"]]
511 perm_deletions = [[user.user_id, None, "user"]]
501 try:
512 try:
502 RepoGroupModel().update_permissions(repo_group=repo_group,
513 RepoGroupModel().update_permissions(repo_group=repo_group,
503 perm_deletions=perm_deletions,
514 perm_deletions=perm_deletions,
504 recursive=apply_to_children,
515 recursive=apply_to_children,
505 cur_user=apiuser)
516 cur_user=apiuser)
506 Session().commit()
517 Session().commit()
507 return {
518 return {
508 'msg': 'Revoked perm (recursive:%s) for user: '
519 'msg': 'Revoked perm (recursive:%s) for user: '
509 '`%s` in repo group: `%s`' % (
520 '`%s` in repo group: `%s`' % (
510 apply_to_children, user.username, repo_group.name
521 apply_to_children, user.username, repo_group.name
511 ),
522 ),
512 'success': True
523 'success': True
513 }
524 }
514 except Exception:
525 except Exception:
515 log.exception("Exception occurred while trying revoke user "
526 log.exception("Exception occurred while trying revoke user "
516 "permission from repo group")
527 "permission from repo group")
517 raise JSONRPCError(
528 raise JSONRPCError(
518 'failed to edit permission for user: '
529 'failed to edit permission for user: '
519 '`%s` in repo group: `%s`' % (userid, repo_group.name))
530 '`%s` in repo group: `%s`' % (userid, repo_group.name))
520
531
521
532
522 @jsonrpc_method()
533 @jsonrpc_method()
523 def grant_user_group_permission_to_repo_group(
534 def grant_user_group_permission_to_repo_group(
524 request, apiuser, repogroupid, usergroupid, perm,
535 request, apiuser, repogroupid, usergroupid, perm,
525 apply_to_children=Optional('none'), ):
536 apply_to_children=Optional('none'), ):
526 """
537 """
527 Grant permission for a user group on given repository group, or update
538 Grant permission for a user group on given repository group, or update
528 existing permissions if found.
539 existing permissions if found.
529
540
530 This command can only be run using an |authtoken| with admin
541 This command can only be run using an |authtoken| with admin
531 permissions on the |repo| group.
542 permissions on the |repo| group.
532
543
533 :param apiuser: This is filled automatically from the |authtoken|.
544 :param apiuser: This is filled automatically from the |authtoken|.
534 :type apiuser: AuthUser
545 :type apiuser: AuthUser
535 :param repogroupid: Set the name or id of repository group
546 :param repogroupid: Set the name or id of repository group
536 :type repogroupid: str or int
547 :type repogroupid: str or int
537 :param usergroupid: id of usergroup
548 :param usergroupid: id of usergroup
538 :type usergroupid: str or int
549 :type usergroupid: str or int
539 :param perm: (group.(none|read|write|admin))
550 :param perm: (group.(none|read|write|admin))
540 :type perm: str
551 :type perm: str
541 :param apply_to_children: 'none', 'repos', 'groups', 'all'
552 :param apply_to_children: 'none', 'repos', 'groups', 'all'
542 :type apply_to_children: str
553 :type apply_to_children: str
543
554
544 Example output:
555 Example output:
545
556
546 .. code-block:: bash
557 .. code-block:: bash
547
558
548 id : <id_given_in_input>
559 id : <id_given_in_input>
549 result : {
560 result : {
550 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
561 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
551 "success": true
562 "success": true
552
563
553 }
564 }
554 error : null
565 error : null
555
566
556 Example error output:
567 Example error output:
557
568
558 .. code-block:: bash
569 .. code-block:: bash
559
570
560 id : <id_given_in_input>
571 id : <id_given_in_input>
561 result : null
572 result : null
562 error : {
573 error : {
563 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
574 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
564 }
575 }
565
576
566 """
577 """
567
578
568 repo_group = get_repo_group_or_error(repogroupid)
579 repo_group = get_repo_group_or_error(repogroupid)
569 perm = get_perm_or_error(perm, prefix='group.')
580 perm = get_perm_or_error(perm, prefix='group.')
570 user_group = get_user_group_or_error(usergroupid)
581 user_group = get_user_group_or_error(usergroupid)
571 if not has_superadmin_permission(apiuser):
582 if not has_superadmin_permission(apiuser):
572 # check if we have admin permission for this repo group !
583 validate_repo_group_permissions(
573 _perms = ('group.admin',)
584 apiuser, repogroupid, repo_group, ('group.admin',))
574 if not HasRepoGroupPermissionAnyApi(*_perms)(
575 user=apiuser, group_name=repo_group.group_name):
576 raise JSONRPCError(
577 'repository group `%s` does not exist' % (repogroupid,))
578
585
579 # check if we have at least read permission for this user group !
586 # check if we have at least read permission for this user group !
580 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
587 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
581 if not HasUserGroupPermissionAnyApi(*_perms)(
588 if not HasUserGroupPermissionAnyApi(*_perms)(
582 user=apiuser, user_group_name=user_group.users_group_name):
589 user=apiuser, user_group_name=user_group.users_group_name):
583 raise JSONRPCError(
590 raise JSONRPCError(
584 'user group `%s` does not exist' % (usergroupid,))
591 'user group `%s` does not exist' % (usergroupid,))
585
592
586 apply_to_children = Optional.extract(apply_to_children)
593 apply_to_children = Optional.extract(apply_to_children)
587
594
588 perm_additions = [[user_group.users_group_id, perm, "user_group"]]
595 perm_additions = [[user_group.users_group_id, perm, "user_group"]]
589 try:
596 try:
590 RepoGroupModel().update_permissions(repo_group=repo_group,
597 RepoGroupModel().update_permissions(repo_group=repo_group,
591 perm_additions=perm_additions,
598 perm_additions=perm_additions,
592 recursive=apply_to_children,
599 recursive=apply_to_children,
593 cur_user=apiuser)
600 cur_user=apiuser)
594 Session().commit()
601 Session().commit()
595 return {
602 return {
596 'msg': 'Granted perm: `%s` (recursive:%s) '
603 'msg': 'Granted perm: `%s` (recursive:%s) '
597 'for user group: `%s` in repo group: `%s`' % (
604 'for user group: `%s` in repo group: `%s`' % (
598 perm.permission_name, apply_to_children,
605 perm.permission_name, apply_to_children,
599 user_group.users_group_name, repo_group.name
606 user_group.users_group_name, repo_group.name
600 ),
607 ),
601 'success': True
608 'success': True
602 }
609 }
603 except Exception:
610 except Exception:
604 log.exception("Exception occurred while trying to grant user "
611 log.exception("Exception occurred while trying to grant user "
605 "group permissions to repo group")
612 "group permissions to repo group")
606 raise JSONRPCError(
613 raise JSONRPCError(
607 'failed to edit permission for user group: `%s` in '
614 'failed to edit permission for user group: `%s` in '
608 'repo group: `%s`' % (
615 'repo group: `%s`' % (
609 usergroupid, repo_group.name
616 usergroupid, repo_group.name
610 )
617 )
611 )
618 )
612
619
613
620
614 @jsonrpc_method()
621 @jsonrpc_method()
615 def revoke_user_group_permission_from_repo_group(
622 def revoke_user_group_permission_from_repo_group(
616 request, apiuser, repogroupid, usergroupid,
623 request, apiuser, repogroupid, usergroupid,
617 apply_to_children=Optional('none')):
624 apply_to_children=Optional('none')):
618 """
625 """
619 Revoke permission for user group on given repository.
626 Revoke permission for user group on given repository.
620
627
621 This command can only be run using an |authtoken| with admin
628 This command can only be run using an |authtoken| with admin
622 permissions on the |repo| group.
629 permissions on the |repo| group.
623
630
624 :param apiuser: This is filled automatically from the |authtoken|.
631 :param apiuser: This is filled automatically from the |authtoken|.
625 :type apiuser: AuthUser
632 :type apiuser: AuthUser
626 :param repogroupid: name or id of repository group
633 :param repogroupid: name or id of repository group
627 :type repogroupid: str or int
634 :type repogroupid: str or int
628 :param usergroupid:
635 :param usergroupid:
629 :param apply_to_children: 'none', 'repos', 'groups', 'all'
636 :param apply_to_children: 'none', 'repos', 'groups', 'all'
630 :type apply_to_children: str
637 :type apply_to_children: str
631
638
632 Example output:
639 Example output:
633
640
634 .. code-block:: bash
641 .. code-block:: bash
635
642
636 id : <id_given_in_input>
643 id : <id_given_in_input>
637 result: {
644 result: {
638 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
645 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
639 "success": true
646 "success": true
640 }
647 }
641 error: null
648 error: null
642
649
643 Example error output:
650 Example error output:
644
651
645 .. code-block:: bash
652 .. code-block:: bash
646
653
647 id : <id_given_in_input>
654 id : <id_given_in_input>
648 result : null
655 result : null
649 error : {
656 error : {
650 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
657 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
651 }
658 }
652
659
653
660
654 """
661 """
655
662
656 repo_group = get_repo_group_or_error(repogroupid)
663 repo_group = get_repo_group_or_error(repogroupid)
657 user_group = get_user_group_or_error(usergroupid)
664 user_group = get_user_group_or_error(usergroupid)
658 if not has_superadmin_permission(apiuser):
665 if not has_superadmin_permission(apiuser):
659 # check if we have admin permission for this repo group !
666 validate_repo_group_permissions(
660 _perms = ('group.admin',)
667 apiuser, repogroupid, repo_group, ('group.admin',))
661 if not HasRepoGroupPermissionAnyApi(*_perms)(
662 user=apiuser, group_name=repo_group.group_name):
663 raise JSONRPCError(
664 'repository group `%s` does not exist' % (repogroupid,))
665
668
666 # check if we have at least read permission for this user group !
669 # check if we have at least read permission for this user group !
667 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
670 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
668 if not HasUserGroupPermissionAnyApi(*_perms)(
671 if not HasUserGroupPermissionAnyApi(*_perms)(
669 user=apiuser, user_group_name=user_group.users_group_name):
672 user=apiuser, user_group_name=user_group.users_group_name):
670 raise JSONRPCError(
673 raise JSONRPCError(
671 'user group `%s` does not exist' % (usergroupid,))
674 'user group `%s` does not exist' % (usergroupid,))
672
675
673 apply_to_children = Optional.extract(apply_to_children)
676 apply_to_children = Optional.extract(apply_to_children)
674
677
675 perm_deletions = [[user_group.users_group_id, None, "user_group"]]
678 perm_deletions = [[user_group.users_group_id, None, "user_group"]]
676 try:
679 try:
677 RepoGroupModel().update_permissions(repo_group=repo_group,
680 RepoGroupModel().update_permissions(repo_group=repo_group,
678 perm_deletions=perm_deletions,
681 perm_deletions=perm_deletions,
679 recursive=apply_to_children,
682 recursive=apply_to_children,
680 cur_user=apiuser)
683 cur_user=apiuser)
681 Session().commit()
684 Session().commit()
682 return {
685 return {
683 'msg': 'Revoked perm (recursive:%s) for user group: '
686 'msg': 'Revoked perm (recursive:%s) for user group: '
684 '`%s` in repo group: `%s`' % (
687 '`%s` in repo group: `%s`' % (
685 apply_to_children, user_group.users_group_name,
688 apply_to_children, user_group.users_group_name,
686 repo_group.name
689 repo_group.name
687 ),
690 ),
688 'success': True
691 'success': True
689 }
692 }
690 except Exception:
693 except Exception:
691 log.exception("Exception occurred while trying revoke user group "
694 log.exception("Exception occurred while trying revoke user group "
692 "permissions from repo group")
695 "permissions from repo group")
693 raise JSONRPCError(
696 raise JSONRPCError(
694 'failed to edit permission for user group: '
697 'failed to edit permission for user group: '
695 '`%s` in repo group: `%s`' % (
698 '`%s` in repo group: `%s`' % (
696 user_group.users_group_name, repo_group.name
699 user_group.users_group_name, repo_group.name
697 )
700 )
698 )
701 )
699
702
@@ -1,29 +1,240 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
3 # Copyright (C) 2016-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 import colander
22 import colander
23
23
24
24 from rhodecode.translation import _
25 from rhodecode.model.validation_schema import validators, preparers, types
25 from rhodecode.model.validation_schema import validators, preparers, types
26
26
27
27
28 def get_group_and_repo(repo_name):
29 from rhodecode.model.repo_group import RepoGroupModel
30 return RepoGroupModel()._get_group_name_and_parent(
31 repo_name, get_object=True)
32
33
34 @colander.deferred
35 def deferred_can_write_to_group_validator(node, kw):
36 old_values = kw.get('old_values') or {}
37 request_user = kw.get('user')
38
39 def can_write_group_validator(node, value):
40 from rhodecode.lib.auth import (
41 HasPermissionAny, HasRepoGroupPermissionAny)
42 from rhodecode.model.repo_group import RepoGroupModel
43
44 messages = {
45 'invalid_parent_repo_group':
46 _(u"Parent repository group `{}` does not exist"),
47 # permissions denied we expose as not existing, to prevent
48 # resource discovery
49 'permission_denied_parent_group':
50 _(u"Parent repository group `{}` does not exist"),
51 'permission_denied_root':
52 _(u"You do not have the permission to store "
53 u"repository groups in the root location.")
54 }
55
56 value = value['repo_group_name']
57 parent_group_name = value
58
59 is_root_location = value is types.RootLocation
60
61 # NOT initialized validators, we must call them
62 can_create_repo_groups_at_root = HasPermissionAny(
63 'hg.admin', 'hg.repogroup.create.true')
64
65 if is_root_location:
66 if can_create_repo_groups_at_root(user=request_user):
67 # we can create repo group inside tool-level. No more checks
68 # are required
69 return
70 else:
71 raise colander.Invalid(node, messages['permission_denied_root'])
72
73 # check if the parent repo group actually exists
74 parent_group = None
75 if parent_group_name:
76 parent_group = RepoGroupModel().get_by_group_name(parent_group_name)
77 if value and not parent_group:
78 raise colander.Invalid(
79 node, messages['invalid_parent_repo_group'].format(
80 parent_group_name))
81
82 # check if we have permissions to create new groups under
83 # parent repo group
84 # create repositories with write permission on group is set to true
85 create_on_write = HasPermissionAny(
86 'hg.create.write_on_repogroup.true')(user=request_user)
87
88 group_admin = HasRepoGroupPermissionAny('group.admin')(
89 parent_group_name, 'can write into group validator', user=request_user)
90 group_write = HasRepoGroupPermissionAny('group.write')(
91 parent_group_name, 'can write into group validator', user=request_user)
92
93 # creation by write access is currently disabled. Needs thinking if
94 # we want to allow this...
95 forbidden = not (group_admin or (group_write and create_on_write and 0))
96
97 if parent_group and forbidden:
98 msg = messages['permission_denied_parent_group'].format(
99 parent_group_name)
100 raise colander.Invalid(node, msg)
101
102 return can_write_group_validator
103
104
105 @colander.deferred
106 def deferred_repo_group_owner_validator(node, kw):
107
108 def repo_owner_validator(node, value):
109 from rhodecode.model.db import User
110 existing = User.get_by_username(value)
111 if not existing:
112 msg = _(u'Repo group owner with id `{}` does not exists').format(
113 value)
114 raise colander.Invalid(node, msg)
115
116 return repo_owner_validator
117
118
119 @colander.deferred
120 def deferred_unique_name_validator(node, kw):
121 request_user = kw.get('user')
122 old_values = kw.get('old_values') or {}
123
124 def unique_name_validator(node, value):
125 from rhodecode.model.db import Repository, RepoGroup
126 name_changed = value != old_values.get('group_name')
127
128 existing = Repository.get_by_repo_name(value)
129 if name_changed and existing:
130 msg = _(u'Repository with name `{}` already exists').format(value)
131 raise colander.Invalid(node, msg)
132
133 existing_group = RepoGroup.get_by_group_name(value)
134 if name_changed and existing_group:
135 msg = _(u'Repository group with name `{}` already exists').format(
136 value)
137 raise colander.Invalid(node, msg)
138 return unique_name_validator
139
140
141 @colander.deferred
142 def deferred_repo_group_name_validator(node, kw):
143 return validators.valid_name_validator
144
145
146 class GroupType(colander.Mapping):
147 def _validate(self, node, value):
148 try:
149 return dict(repo_group_name=value)
150 except Exception as e:
151 raise colander.Invalid(
152 node, '"${val}" is not a mapping type: ${err}'.format(
153 val=value, err=e))
154
155 def deserialize(self, node, cstruct):
156 if cstruct is colander.null:
157 return cstruct
158
159 appstruct = super(GroupType, self).deserialize(node, cstruct)
160 validated_name = appstruct['repo_group_name']
161
162 # inject group based on once deserialized data
163 (repo_group_name_without_group,
164 parent_group_name,
165 parent_group) = get_group_and_repo(validated_name)
166
167 appstruct['repo_group_name_without_group'] = repo_group_name_without_group
168 appstruct['repo_group_name'] = parent_group_name or types.RootLocation
169 if parent_group:
170 appstruct['repo_group_id'] = parent_group.group_id
171
172 return appstruct
173
174
175 class GroupSchema(colander.SchemaNode):
176 schema_type = GroupType
177 validator = deferred_can_write_to_group_validator
178 missing = colander.null
179
180
181 class RepoGroup(GroupSchema):
182 repo_group_name = colander.SchemaNode(
183 types.GroupNameType())
184 repo_group_id = colander.SchemaNode(
185 colander.String(), missing=None)
186 repo_group_name_without_group = colander.SchemaNode(
187 colander.String(), missing=None)
188
189
190 class RepoGroupAccessSchema(colander.MappingSchema):
191 repo_group = RepoGroup()
192
193
194 class RepoGroupNameUniqueSchema(colander.MappingSchema):
195 unique_repo_group_name = colander.SchemaNode(
196 colander.String(),
197 validator=deferred_unique_name_validator)
198
199
28 class RepoGroupSchema(colander.Schema):
200 class RepoGroupSchema(colander.Schema):
29 group_name = colander.SchemaNode(types.GroupNameType())
201
202 repo_group_name = colander.SchemaNode(
203 types.GroupNameType(),
204 validator=deferred_repo_group_name_validator)
205
206 repo_group_owner = colander.SchemaNode(
207 colander.String(),
208 validator=deferred_repo_group_owner_validator)
209
210 repo_group_description = colander.SchemaNode(
211 colander.String(), missing='')
212
213 repo_group_copy_permissions = colander.SchemaNode(
214 types.StringBooleanType(),
215 missing=False)
216
217 repo_group_enable_locking = colander.SchemaNode(
218 types.StringBooleanType(),
219 missing=False)
220
221 def deserialize(self, cstruct):
222 """
223 Custom deserialize that allows to chain validation, and verify
224 permissions, and as last step uniqueness
225 """
226
227 appstruct = super(RepoGroupSchema, self).deserialize(cstruct)
228 validated_name = appstruct['repo_group_name']
229
230 # second pass to validate permissions to repo_group
231 second = RepoGroupAccessSchema().bind(**self.bindings)
232 appstruct_second = second.deserialize({'repo_group': validated_name})
233 # save result
234 appstruct['repo_group'] = appstruct_second['repo_group']
235
236 # thirds to validate uniqueness
237 third = RepoGroupNameUniqueSchema().bind(**self.bindings)
238 third.deserialize({'unique_repo_group_name': validated_name})
239
240 return appstruct
General Comments 0
You need to be logged in to leave comments. Login now