##// END OF EJS Templates
bugfix: fixed #3965 and updated a bunch of api tests which...
dan -
r68:36f83786 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,144 +1,161 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 mock
22 22 import pytest
23 23
24 24 from rhodecode.model.repo import RepoModel
25 25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
26 26 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok, crash)
27 build_data, api_call, assert_error, assert_ok, crash, jsonify)
28 28 from rhodecode.tests.fixture import Fixture
29 29
30 30
31 31 fixture = Fixture()
32 32
33 UPDATE_REPO_NAME = 'api_update_me'
34
35 class SAME_AS_UPDATES(object): """ Constant used for tests below """
33 36
34 37 @pytest.mark.usefixtures("testuser_api", "app")
35 38 class TestApiUpdateRepo(object):
36 @pytest.mark.parametrize("changing_attr, updates", [
37 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
38 ('description', {'description': 'new description'}),
39 ('active', {'active': True}),
40 ('active', {'active': False}),
41 ('clone_uri', {'clone_uri': 'http://foo.com/repo'}),
42 ('clone_uri', {'clone_uri': None}),
43 ('landing_rev', {'landing_rev': 'branch:master'}),
44 ('enable_statistics', {'enable_statistics': True}),
45 ('enable_locking', {'enable_locking': True}),
46 ('enable_downloads', {'enable_downloads': True}),
47 ('name', {'name': 'new_repo_name'}),
48 ('repo_group', {'group': 'test_group_for_update'}),
39
40 @pytest.mark.parametrize("changing_attr, updates, expected", [
41 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}, SAME_AS_UPDATES),
42 ('description', {'description': 'new description'}, SAME_AS_UPDATES),
43 ('clone_uri', {'clone_uri': 'http://foo.com/repo'}, SAME_AS_UPDATES),
44 ('clone_uri', {'clone_uri': None}, {'clone_uri': ''}),
45 ('clone_uri', {'clone_uri': ''}, {'clone_uri': ''}),
46 ('landing_rev', {'landing_rev': 'branch:master'},
47 {'landing_rev': ['branch', 'master']}),
48 ('enable_statistics', {'enable_statistics': True}, SAME_AS_UPDATES),
49 ('enable_locking', {'enable_locking': True}, SAME_AS_UPDATES),
50 ('enable_downloads', {'enable_downloads': True}, SAME_AS_UPDATES),
51 ('name', {'name': 'new_repo_name'},
52 {'repo_name': 'new_repo_name'}),
53 ('repo_group',
54 {'group': 'test_group_for_update'},
55 {'repo_name': 'test_group_for_update/%s' % UPDATE_REPO_NAME}),
49 56 ])
50 def test_api_update_repo(self, changing_attr, updates, backend):
51 repo_name = 'api_update_me'
57 def test_api_update_repo(self, changing_attr, updates, expected, backend):
58 repo_name = UPDATE_REPO_NAME
52 59 repo = fixture.create_repo(repo_name, repo_type=backend.alias)
53 60 if changing_attr == 'repo_group':
54 61 fixture.create_repo_group(updates['group'])
55 62
63 expected_api_data = repo.get_api_data(include_secrets=True)
64 if expected is SAME_AS_UPDATES:
65 expected_api_data.update(updates)
66 else:
67 expected_api_data.update(expected)
68
69
56 70 id_, params = build_data(
57 71 self.apikey, 'update_repo', repoid=repo_name, **updates)
58 72 response = api_call(self.app, params)
73
59 74 if changing_attr == 'name':
60 75 repo_name = updates['name']
61 76 if changing_attr == 'repo_group':
62 77 repo_name = '/'.join([updates['group'], repo_name])
78
63 79 try:
64 80 expected = {
65 81 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
66 'repository': repo.get_api_data(include_secrets=True)
82 'repository': jsonify(expected_api_data)
67 83 }
68 84 assert_ok(id_, expected, given=response.body)
69 85 finally:
70 86 fixture.destroy_repo(repo_name)
71 87 if changing_attr == 'repo_group':
72
73 88 fixture.destroy_repo_group(updates['group'])
74 89
75 90 def test_api_update_repo_fork_of_field(self, backend):
76 91 master_repo = backend.create_repo()
77 92 repo = backend.create_repo()
78
79 93 updates = {
80 94 'fork_of': master_repo.repo_name
81 95 }
96 expected_api_data = repo.get_api_data(include_secrets=True)
97 expected_api_data.update(updates)
98
82 99 id_, params = build_data(
83 100 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
84 101 response = api_call(self.app, params)
85 102 expected = {
86 103 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
87 'repository': repo.get_api_data(include_secrets=True)
104 'repository': jsonify(expected_api_data)
88 105 }
89 106 assert_ok(id_, expected, given=response.body)
90 107 result = response.json['result']['repository']
91 108 assert result['fork_of'] == master_repo.repo_name
92 109
93 110 def test_api_update_repo_fork_of_not_found(self, backend):
94 111 master_repo_name = 'fake-parent-repo'
95 112 repo = backend.create_repo()
96 113 updates = {
97 114 'fork_of': master_repo_name
98 115 }
99 116 id_, params = build_data(
100 117 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
101 118 response = api_call(self.app, params)
102 119 expected = 'repository `{}` does not exist'.format(master_repo_name)
103 120 assert_error(id_, expected, given=response.body)
104 121
105 122 def test_api_update_repo_with_repo_group_not_existing(self):
106 123 repo_name = 'admin_owned'
107 124 fixture.create_repo(repo_name)
108 125 updates = {'group': 'test_group_for_update'}
109 126 id_, params = build_data(
110 127 self.apikey, 'update_repo', repoid=repo_name, **updates)
111 128 response = api_call(self.app, params)
112 129 try:
113 130 expected = 'repository group `%s` does not exist' % (
114 131 updates['group'],)
115 132 assert_error(id_, expected, given=response.body)
116 133 finally:
117 134 fixture.destroy_repo(repo_name)
118 135
119 136 def test_api_update_repo_regular_user_not_allowed(self):
120 137 repo_name = 'admin_owned'
121 138 fixture.create_repo(repo_name)
122 139 updates = {'active': False}
123 140 id_, params = build_data(
124 141 self.apikey_regular, 'update_repo', repoid=repo_name, **updates)
125 142 response = api_call(self.app, params)
126 143 try:
127 144 expected = 'repository `%s` does not exist' % (repo_name,)
128 145 assert_error(id_, expected, given=response.body)
129 146 finally:
130 147 fixture.destroy_repo(repo_name)
131 148
132 149 @mock.patch.object(RepoModel, 'update', crash)
133 150 def test_api_update_repo_exception_occurred(self, backend):
134 repo_name = 'api_update_me'
151 repo_name = UPDATE_REPO_NAME
135 152 fixture.create_repo(repo_name, repo_type=backend.alias)
136 153 id_, params = build_data(
137 154 self.apikey, 'update_repo', repoid=repo_name,
138 155 owner=TEST_USER_ADMIN_LOGIN,)
139 156 response = api_call(self.app, params)
140 157 try:
141 158 expected = 'failed to update repo `%s`' % (repo_name,)
142 159 assert_error(id_, expected, given=response.body)
143 160 finally:
144 161 fixture.destroy_repo(repo_name)
@@ -1,100 +1,108 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 mock
22 22 import pytest
23 23
24 24 from rhodecode.model.user import UserModel
25 25 from rhodecode.model.user_group import UserGroupModel
26 26 from rhodecode.tests import TEST_USER_REGULAR_LOGIN
27 27 from rhodecode.api.tests.utils import (
28 build_data, api_call, assert_error, assert_ok, crash)
28 build_data, api_call, assert_error, assert_ok, crash, jsonify)
29 29
30 30
31 31 @pytest.mark.usefixtures("testuser_api", "app")
32 32 class TestUpdateUserGroup(object):
33 33 @pytest.mark.parametrize("changing_attr, updates", [
34 34 ('group_name', {'group_name': 'new_group_name'}),
35 35 ('group_name', {'group_name': 'test_group_for_update'}),
36 36 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
37 37 ('active', {'active': False}),
38 38 ('active', {'active': True})
39 39 ])
40 40 def test_api_update_user_group(self, changing_attr, updates, user_util):
41 41 user_group = user_util.create_user_group()
42 42 group_name = user_group.users_group_name
43 expected_api_data = user_group.get_api_data()
44 expected_api_data.update(updates)
45
43 46 id_, params = build_data(
44 47 self.apikey, 'update_user_group', usergroupid=group_name,
45 48 **updates)
46 49 response = api_call(self.app, params)
50
47 51 expected = {
48 52 'msg': 'updated user group ID:%s %s' % (
49 53 user_group.users_group_id, user_group.users_group_name),
50 'user_group': user_group.get_api_data()
54 'user_group': jsonify(expected_api_data)
51 55 }
52 56 assert_ok(id_, expected, given=response.body)
53 57
54 58 @pytest.mark.parametrize("changing_attr, updates", [
55 59 # TODO: mikhail: decide if we need to test against the commented params
56 60 # ('group_name', {'group_name': 'new_group_name'}),
57 61 # ('group_name', {'group_name': 'test_group_for_update'}),
58 62 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
59 63 ('active', {'active': False}),
60 64 ('active', {'active': True})
61 65 ])
62 66 def test_api_update_user_group_regular_user(
63 67 self, changing_attr, updates, user_util):
64 68 user_group = user_util.create_user_group()
65 69 group_name = user_group.users_group_name
70 expected_api_data = user_group.get_api_data()
71 expected_api_data.update(updates)
72
73
66 74 # grant permission to this user
67 75 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
68 76
69 77 user_util.grant_user_permission_to_user_group(
70 78 user_group, user, 'usergroup.admin')
71 79 id_, params = build_data(
72 80 self.apikey_regular, 'update_user_group',
73 81 usergroupid=group_name, **updates)
74 82 response = api_call(self.app, params)
75 83 expected = {
76 84 'msg': 'updated user group ID:%s %s' % (
77 85 user_group.users_group_id, user_group.users_group_name),
78 'user_group': user_group.get_api_data()
86 'user_group': jsonify(expected_api_data)
79 87 }
80 88 assert_ok(id_, expected, given=response.body)
81 89
82 90 def test_api_update_user_group_regular_user_no_permission(self, user_util):
83 91 user_group = user_util.create_user_group()
84 92 group_name = user_group.users_group_name
85 93 id_, params = build_data(
86 94 self.apikey_regular, 'update_user_group', usergroupid=group_name)
87 95 response = api_call(self.app, params)
88 96
89 97 expected = 'user group `%s` does not exist' % (group_name)
90 98 assert_error(id_, expected, given=response.body)
91 99
92 100 @mock.patch.object(UserGroupModel, 'update', crash)
93 101 def test_api_update_user_group_exception_occurred(self, user_util):
94 102 user_group = user_util.create_user_group()
95 103 group_name = user_group.users_group_name
96 104 id_, params = build_data(
97 105 self.apikey, 'update_user_group', usergroupid=group_name)
98 106 response = api_call(self.app, params)
99 107 expected = 'failed to update user group `%s`' % (group_name,)
100 108 assert_error(id_, expected, given=response.body)
@@ -1,1777 +1,1779 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2016 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 time
23 23
24 24 import colander
25 25
26 26 from rhodecode import BACKENDS
27 27 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, json
28 28 from rhodecode.api.utils import (
29 29 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
30 30 get_user_group_or_error, get_user_or_error, has_repo_permissions,
31 31 get_perm_or_error, store_update, get_repo_group_or_error, parse_args,
32 32 get_origin, build_commit_data)
33 33 from rhodecode.lib.auth import (
34 34 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
35 35 HasUserGroupPermissionAnyApi)
36 36 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
37 37 from rhodecode.lib.utils import map_groups
38 38 from rhodecode.lib.utils2 import str2bool, time_to_datetime
39 39 from rhodecode.model.changeset_status import ChangesetStatusModel
40 40 from rhodecode.model.comment import ChangesetCommentsModel
41 41 from rhodecode.model.db import (
42 42 Session, ChangesetStatus, RepositoryField, Repository)
43 43 from rhodecode.model.repo import RepoModel
44 44 from rhodecode.model.repo_group import RepoGroupModel
45 45 from rhodecode.model.scm import ScmModel, RepoList
46 46 from rhodecode.model.settings import SettingsModel
47 47 from rhodecode.model.validation_schema import RepoSchema
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 @jsonrpc_method()
53 53 def get_repo(request, apiuser, repoid, cache=Optional(True)):
54 54 """
55 55 Gets an existing repository by its name or repository_id.
56 56
57 57 The members section so the output returns users groups or users
58 58 associated with that repository.
59 59
60 60 This command can only be run using an |authtoken| with admin rights,
61 61 or users with at least read rights to the |repo|.
62 62
63 63 :param apiuser: This is filled automatically from the |authtoken|.
64 64 :type apiuser: AuthUser
65 65 :param repoid: The repository name or repository id.
66 66 :type repoid: str or int
67 67 :param cache: use the cached value for last changeset
68 68 :type: cache: Optional(bool)
69 69
70 70 Example output:
71 71
72 72 .. code-block:: bash
73 73
74 74 {
75 75 "error": null,
76 76 "id": <repo_id>,
77 77 "result": {
78 78 "clone_uri": null,
79 79 "created_on": "timestamp",
80 80 "description": "repo description",
81 81 "enable_downloads": false,
82 82 "enable_locking": false,
83 83 "enable_statistics": false,
84 84 "followers": [
85 85 {
86 86 "active": true,
87 87 "admin": false,
88 88 "api_key": "****************************************",
89 89 "api_keys": [
90 90 "****************************************"
91 91 ],
92 92 "email": "user@example.com",
93 93 "emails": [
94 94 "user@example.com"
95 95 ],
96 96 "extern_name": "rhodecode",
97 97 "extern_type": "rhodecode",
98 98 "firstname": "username",
99 99 "ip_addresses": [],
100 100 "language": null,
101 101 "last_login": "2015-09-16T17:16:35.854",
102 102 "lastname": "surname",
103 103 "user_id": <user_id>,
104 104 "username": "name"
105 105 }
106 106 ],
107 107 "fork_of": "parent-repo",
108 108 "landing_rev": [
109 109 "rev",
110 110 "tip"
111 111 ],
112 112 "last_changeset": {
113 113 "author": "User <user@example.com>",
114 114 "branch": "default",
115 115 "date": "timestamp",
116 116 "message": "last commit message",
117 117 "parents": [
118 118 {
119 119 "raw_id": "commit-id"
120 120 }
121 121 ],
122 122 "raw_id": "commit-id",
123 123 "revision": <revision number>,
124 124 "short_id": "short id"
125 125 },
126 126 "lock_reason": null,
127 127 "locked_by": null,
128 128 "locked_date": null,
129 129 "members": [
130 130 {
131 131 "name": "super-admin-name",
132 132 "origin": "super-admin",
133 133 "permission": "repository.admin",
134 134 "type": "user"
135 135 },
136 136 {
137 137 "name": "owner-name",
138 138 "origin": "owner",
139 139 "permission": "repository.admin",
140 140 "type": "user"
141 141 },
142 142 {
143 143 "name": "user-group-name",
144 144 "origin": "permission",
145 145 "permission": "repository.write",
146 146 "type": "user_group"
147 147 }
148 148 ],
149 149 "owner": "owner-name",
150 150 "permissions": [
151 151 {
152 152 "name": "super-admin-name",
153 153 "origin": "super-admin",
154 154 "permission": "repository.admin",
155 155 "type": "user"
156 156 },
157 157 {
158 158 "name": "owner-name",
159 159 "origin": "owner",
160 160 "permission": "repository.admin",
161 161 "type": "user"
162 162 },
163 163 {
164 164 "name": "user-group-name",
165 165 "origin": "permission",
166 166 "permission": "repository.write",
167 167 "type": "user_group"
168 168 }
169 169 ],
170 170 "private": true,
171 171 "repo_id": 676,
172 172 "repo_name": "user-group/repo-name",
173 173 "repo_type": "hg"
174 174 }
175 175 }
176 176 """
177 177
178 178 repo = get_repo_or_error(repoid)
179 179 cache = Optional.extract(cache)
180 180 include_secrets = False
181 181 if has_superadmin_permission(apiuser):
182 182 include_secrets = True
183 183 else:
184 184 # check if we have at least read permission for this repo !
185 185 _perms = (
186 186 'repository.admin', 'repository.write', 'repository.read',)
187 187 has_repo_permissions(apiuser, repoid, repo, _perms)
188 188
189 189 permissions = []
190 190 for _user in repo.permissions():
191 191 user_data = {
192 192 'name': _user.username,
193 193 'permission': _user.permission,
194 194 'origin': get_origin(_user),
195 195 'type': "user",
196 196 }
197 197 permissions.append(user_data)
198 198
199 199 for _user_group in repo.permission_user_groups():
200 200 user_group_data = {
201 201 'name': _user_group.users_group_name,
202 202 'permission': _user_group.permission,
203 203 'origin': get_origin(_user_group),
204 204 'type': "user_group",
205 205 }
206 206 permissions.append(user_group_data)
207 207
208 208 following_users = [
209 209 user.user.get_api_data(include_secrets=include_secrets)
210 210 for user in repo.followers]
211 211
212 212 if not cache:
213 213 repo.update_commit_cache()
214 214 data = repo.get_api_data(include_secrets=include_secrets)
215 215 data['members'] = permissions # TODO: this should be deprecated soon
216 216 data['permissions'] = permissions
217 217 data['followers'] = following_users
218 218 return data
219 219
220 220
221 221 @jsonrpc_method()
222 222 def get_repos(request, apiuser):
223 223 """
224 224 Lists all existing repositories.
225 225
226 226 This command can only be run using an |authtoken| with admin rights,
227 227 or users with at least read rights to |repos|.
228 228
229 229 :param apiuser: This is filled automatically from the |authtoken|.
230 230 :type apiuser: AuthUser
231 231
232 232 Example output:
233 233
234 234 .. code-block:: bash
235 235
236 236 id : <id_given_in_input>
237 237 result: [
238 238 {
239 239 "repo_id" : "<repo_id>",
240 240 "repo_name" : "<reponame>"
241 241 "repo_type" : "<repo_type>",
242 242 "clone_uri" : "<clone_uri>",
243 243 "private": : "<bool>",
244 244 "created_on" : "<datetimecreated>",
245 245 "description" : "<description>",
246 246 "landing_rev": "<landing_rev>",
247 247 "owner": "<repo_owner>",
248 248 "fork_of": "<name_of_fork_parent>",
249 249 "enable_downloads": "<bool>",
250 250 "enable_locking": "<bool>",
251 251 "enable_statistics": "<bool>",
252 252 },
253 253 ...
254 254 ]
255 255 error: null
256 256 """
257 257
258 258 include_secrets = has_superadmin_permission(apiuser)
259 259 _perms = ('repository.read', 'repository.write', 'repository.admin',)
260 260 extras = {'user': apiuser}
261 261
262 262 repo_list = RepoList(
263 263 RepoModel().get_all(), perm_set=_perms, extra_kwargs=extras)
264 264 return [repo.get_api_data(include_secrets=include_secrets)
265 265 for repo in repo_list]
266 266
267 267
268 268 @jsonrpc_method()
269 269 def get_repo_changeset(request, apiuser, repoid, revision,
270 270 details=Optional('basic')):
271 271 """
272 272 Returns information about a changeset.
273 273
274 274 Additionally parameters define the amount of details returned by
275 275 this function.
276 276
277 277 This command can only be run using an |authtoken| with admin rights,
278 278 or users with at least read rights to the |repo|.
279 279
280 280 :param apiuser: This is filled automatically from the |authtoken|.
281 281 :type apiuser: AuthUser
282 282 :param repoid: The repository name or repository id
283 283 :type repoid: str or int
284 284 :param revision: revision for which listing should be done
285 285 :type revision: str
286 286 :param details: details can be 'basic|extended|full' full gives diff
287 287 info details like the diff itself, and number of changed files etc.
288 288 :type details: Optional(str)
289 289
290 290 """
291 291 repo = get_repo_or_error(repoid)
292 292 if not has_superadmin_permission(apiuser):
293 293 _perms = (
294 294 'repository.admin', 'repository.write', 'repository.read',)
295 295 has_repo_permissions(apiuser, repoid, repo, _perms)
296 296
297 297 changes_details = Optional.extract(details)
298 298 _changes_details_types = ['basic', 'extended', 'full']
299 299 if changes_details not in _changes_details_types:
300 300 raise JSONRPCError(
301 301 'ret_type must be one of %s' % (
302 302 ','.join(_changes_details_types)))
303 303
304 304 pre_load = ['author', 'branch', 'date', 'message', 'parents',
305 305 'status', '_commit', '_file_paths']
306 306
307 307 try:
308 308 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
309 309 except TypeError as e:
310 310 raise JSONRPCError(e.message)
311 311 _cs_json = cs.__json__()
312 312 _cs_json['diff'] = build_commit_data(cs, changes_details)
313 313 if changes_details == 'full':
314 314 _cs_json['refs'] = {
315 315 'branches': [cs.branch],
316 316 'bookmarks': getattr(cs, 'bookmarks', []),
317 317 'tags': cs.tags
318 318 }
319 319 return _cs_json
320 320
321 321
322 322 @jsonrpc_method()
323 323 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
324 324 details=Optional('basic')):
325 325 """
326 326 Returns a set of commits limited by the number starting
327 327 from the `start_rev` option.
328 328
329 329 Additional parameters define the amount of details returned by this
330 330 function.
331 331
332 332 This command can only be run using an |authtoken| with admin rights,
333 333 or users with at least read rights to |repos|.
334 334
335 335 :param apiuser: This is filled automatically from the |authtoken|.
336 336 :type apiuser: AuthUser
337 337 :param repoid: The repository name or repository ID.
338 338 :type repoid: str or int
339 339 :param start_rev: The starting revision from where to get changesets.
340 340 :type start_rev: str
341 341 :param limit: Limit the number of commits to this amount
342 342 :type limit: str or int
343 343 :param details: Set the level of detail returned. Valid option are:
344 344 ``basic``, ``extended`` and ``full``.
345 345 :type details: Optional(str)
346 346
347 347 .. note::
348 348
349 349 Setting the parameter `details` to the value ``full`` is extensive
350 350 and returns details like the diff itself, and the number
351 351 of changed files.
352 352
353 353 """
354 354 repo = get_repo_or_error(repoid)
355 355 if not has_superadmin_permission(apiuser):
356 356 _perms = (
357 357 'repository.admin', 'repository.write', 'repository.read',)
358 358 has_repo_permissions(apiuser, repoid, repo, _perms)
359 359
360 360 changes_details = Optional.extract(details)
361 361 _changes_details_types = ['basic', 'extended', 'full']
362 362 if changes_details not in _changes_details_types:
363 363 raise JSONRPCError(
364 364 'ret_type must be one of %s' % (
365 365 ','.join(_changes_details_types)))
366 366
367 367 limit = int(limit)
368 368 pre_load = ['author', 'branch', 'date', 'message', 'parents',
369 369 'status', '_commit', '_file_paths']
370 370
371 371 vcs_repo = repo.scm_instance()
372 372 # SVN needs a special case to distinguish its index and commit id
373 373 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
374 374 start_rev = vcs_repo.commit_ids[0]
375 375
376 376 try:
377 377 commits = vcs_repo.get_commits(
378 378 start_id=start_rev, pre_load=pre_load)
379 379 except TypeError as e:
380 380 raise JSONRPCError(e.message)
381 381 except Exception:
382 382 log.exception('Fetching of commits failed')
383 383 raise JSONRPCError('Error occurred during commit fetching')
384 384
385 385 ret = []
386 386 for cnt, commit in enumerate(commits):
387 387 if cnt >= limit != -1:
388 388 break
389 389 _cs_json = commit.__json__()
390 390 _cs_json['diff'] = build_commit_data(commit, changes_details)
391 391 if changes_details == 'full':
392 392 _cs_json['refs'] = {
393 393 'branches': [commit.branch],
394 394 'bookmarks': getattr(commit, 'bookmarks', []),
395 395 'tags': commit.tags
396 396 }
397 397 ret.append(_cs_json)
398 398 return ret
399 399
400 400
401 401 @jsonrpc_method()
402 402 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
403 403 ret_type=Optional('all'), details=Optional('basic')):
404 404 """
405 405 Returns a list of nodes and children in a flat list for a given
406 406 path at given revision.
407 407
408 408 It's possible to specify ret_type to show only `files` or `dirs`.
409 409
410 410 This command can only be run using an |authtoken| with admin rights,
411 411 or users with at least read rights to |repos|.
412 412
413 413 :param apiuser: This is filled automatically from the |authtoken|.
414 414 :type apiuser: AuthUser
415 415 :param repoid: The repository name or repository ID.
416 416 :type repoid: str or int
417 417 :param revision: The revision for which listing should be done.
418 418 :type revision: str
419 419 :param root_path: The path from which to start displaying.
420 420 :type root_path: str
421 421 :param ret_type: Set the return type. Valid options are
422 422 ``all`` (default), ``files`` and ``dirs``.
423 423 :type ret_type: Optional(str)
424 424 :param details: Returns extended information about nodes, such as
425 425 md5, binary, and or content. The valid options are ``basic`` and
426 426 ``full``.
427 427 :type details: Optional(str)
428 428
429 429 Example output:
430 430
431 431 .. code-block:: bash
432 432
433 433 id : <id_given_in_input>
434 434 result: [
435 435 {
436 436 "name" : "<name>"
437 437 "type" : "<type>",
438 438 "binary": "<true|false>" (only in extended mode)
439 439 "md5" : "<md5 of file content>" (only in extended mode)
440 440 },
441 441 ...
442 442 ]
443 443 error: null
444 444 """
445 445
446 446 repo = get_repo_or_error(repoid)
447 447 if not has_superadmin_permission(apiuser):
448 448 _perms = (
449 449 'repository.admin', 'repository.write', 'repository.read',)
450 450 has_repo_permissions(apiuser, repoid, repo, _perms)
451 451
452 452 ret_type = Optional.extract(ret_type)
453 453 details = Optional.extract(details)
454 454 _extended_types = ['basic', 'full']
455 455 if details not in _extended_types:
456 456 raise JSONRPCError(
457 457 'ret_type must be one of %s' % (','.join(_extended_types)))
458 458 extended_info = False
459 459 content = False
460 460 if details == 'basic':
461 461 extended_info = True
462 462
463 463 if details == 'full':
464 464 extended_info = content = True
465 465
466 466 _map = {}
467 467 try:
468 468 # check if repo is not empty by any chance, skip quicker if it is.
469 469 _scm = repo.scm_instance()
470 470 if _scm.is_empty():
471 471 return []
472 472
473 473 _d, _f = ScmModel().get_nodes(
474 474 repo, revision, root_path, flat=False,
475 475 extended_info=extended_info, content=content)
476 476 _map = {
477 477 'all': _d + _f,
478 478 'files': _f,
479 479 'dirs': _d,
480 480 }
481 481 return _map[ret_type]
482 482 except KeyError:
483 483 raise JSONRPCError(
484 484 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
485 485 except Exception:
486 486 log.exception("Exception occurred while trying to get repo nodes")
487 487 raise JSONRPCError(
488 488 'failed to get repo: `%s` nodes' % repo.repo_name
489 489 )
490 490
491 491
492 492 @jsonrpc_method()
493 493 def get_repo_refs(request, apiuser, repoid):
494 494 """
495 495 Returns a dictionary of current references. It returns
496 496 bookmarks, branches, closed_branches, and tags for given repository
497 497
498 498 It's possible to specify ret_type to show only `files` or `dirs`.
499 499
500 500 This command can only be run using an |authtoken| with admin rights,
501 501 or users with at least read rights to |repos|.
502 502
503 503 :param apiuser: This is filled automatically from the |authtoken|.
504 504 :type apiuser: AuthUser
505 505 :param repoid: The repository name or repository ID.
506 506 :type repoid: str or int
507 507
508 508 Example output:
509 509
510 510 .. code-block:: bash
511 511
512 512 id : <id_given_in_input>
513 513 result: [
514 514 TODO...
515 515 ]
516 516 error: null
517 517 """
518 518
519 519 repo = get_repo_or_error(repoid)
520 520 if not has_superadmin_permission(apiuser):
521 521 _perms = ('repository.admin', 'repository.write', 'repository.read',)
522 522 has_repo_permissions(apiuser, repoid, repo, _perms)
523 523
524 524 try:
525 525 # check if repo is not empty by any chance, skip quicker if it is.
526 526 vcs_instance = repo.scm_instance()
527 527 refs = vcs_instance.refs()
528 528 return refs
529 529 except Exception:
530 530 log.exception("Exception occurred while trying to get repo refs")
531 531 raise JSONRPCError(
532 532 'failed to get repo: `%s` references' % repo.repo_name
533 533 )
534 534
535 535
536 536 @jsonrpc_method()
537 537 def create_repo(request, apiuser, repo_name, repo_type,
538 538 owner=Optional(OAttr('apiuser')), description=Optional(''),
539 539 private=Optional(False), clone_uri=Optional(None),
540 540 landing_rev=Optional('rev:tip'),
541 541 enable_statistics=Optional(False),
542 542 enable_locking=Optional(False),
543 543 enable_downloads=Optional(False),
544 544 copy_permissions=Optional(False)):
545 545 """
546 546 Creates a repository.
547 547
548 548 * If the repository name contains "/", all the required repository
549 549 groups will be created.
550 550
551 551 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
552 552 (with "foo" as parent). It will also create the "baz" repository
553 553 with "bar" as |repo| group.
554 554
555 555 This command can only be run using an |authtoken| with at least
556 556 write permissions to the |repo|.
557 557
558 558 :param apiuser: This is filled automatically from the |authtoken|.
559 559 :type apiuser: AuthUser
560 560 :param repo_name: Set the repository name.
561 561 :type repo_name: str
562 562 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
563 563 :type repo_type: str
564 564 :param owner: user_id or username
565 565 :type owner: Optional(str)
566 566 :param description: Set the repository description.
567 567 :type description: Optional(str)
568 568 :param private:
569 569 :type private: bool
570 570 :param clone_uri:
571 571 :type clone_uri: str
572 572 :param landing_rev: <rev_type>:<rev>
573 573 :type landing_rev: str
574 574 :param enable_locking:
575 575 :type enable_locking: bool
576 576 :param enable_downloads:
577 577 :type enable_downloads: bool
578 578 :param enable_statistics:
579 579 :type enable_statistics: bool
580 580 :param copy_permissions: Copy permission from group in which the
581 581 repository is being created.
582 582 :type copy_permissions: bool
583 583
584 584
585 585 Example output:
586 586
587 587 .. code-block:: bash
588 588
589 589 id : <id_given_in_input>
590 590 result: {
591 591 "msg": "Created new repository `<reponame>`",
592 592 "success": true,
593 593 "task": "<celery task id or None if done sync>"
594 594 }
595 595 error: null
596 596
597 597
598 598 Example error output:
599 599
600 600 .. code-block:: bash
601 601
602 602 id : <id_given_in_input>
603 603 result : null
604 604 error : {
605 605 'failed to create repository `<repo_name>`
606 606 }
607 607
608 608 """
609 609 schema = RepoSchema()
610 610 try:
611 611 data = schema.deserialize({
612 612 'repo_name': repo_name
613 613 })
614 614 except colander.Invalid as e:
615 615 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
616 616 repo_name = data['repo_name']
617 617
618 618 (repo_name_cleaned,
619 619 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
620 620 repo_name)
621 621
622 622 if not HasPermissionAnyApi(
623 623 'hg.admin', 'hg.create.repository')(user=apiuser):
624 624 # check if we have admin permission for this repo group if given !
625 625
626 626 if parent_group_name:
627 627 repogroupid = parent_group_name
628 628 repo_group = get_repo_group_or_error(parent_group_name)
629 629
630 630 _perms = ('group.admin',)
631 631 if not HasRepoGroupPermissionAnyApi(*_perms)(
632 632 user=apiuser, group_name=repo_group.group_name):
633 633 raise JSONRPCError(
634 634 'repository group `%s` does not exist' % (
635 635 repogroupid,))
636 636 else:
637 637 raise JSONRPCForbidden()
638 638
639 639 if not has_superadmin_permission(apiuser):
640 640 if not isinstance(owner, Optional):
641 641 # forbid setting owner for non-admins
642 642 raise JSONRPCError(
643 643 'Only RhodeCode admin can specify `owner` param')
644 644
645 645 if isinstance(owner, Optional):
646 646 owner = apiuser.user_id
647 647
648 648 owner = get_user_or_error(owner)
649 649
650 650 if RepoModel().get_by_repo_name(repo_name):
651 651 raise JSONRPCError("repo `%s` already exist" % repo_name)
652 652
653 653 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
654 654 if isinstance(private, Optional):
655 655 private = defs.get('repo_private') or Optional.extract(private)
656 656 if isinstance(repo_type, Optional):
657 657 repo_type = defs.get('repo_type')
658 658 if isinstance(enable_statistics, Optional):
659 659 enable_statistics = defs.get('repo_enable_statistics')
660 660 if isinstance(enable_locking, Optional):
661 661 enable_locking = defs.get('repo_enable_locking')
662 662 if isinstance(enable_downloads, Optional):
663 663 enable_downloads = defs.get('repo_enable_downloads')
664 664
665 665 clone_uri = Optional.extract(clone_uri)
666 666 description = Optional.extract(description)
667 667 landing_rev = Optional.extract(landing_rev)
668 668 copy_permissions = Optional.extract(copy_permissions)
669 669
670 670 try:
671 671 # create structure of groups and return the last group
672 672 repo_group = map_groups(repo_name)
673 673 data = {
674 674 'repo_name': repo_name_cleaned,
675 675 'repo_name_full': repo_name,
676 676 'repo_type': repo_type,
677 677 'repo_description': description,
678 678 'owner': owner,
679 679 'repo_private': private,
680 680 'clone_uri': clone_uri,
681 681 'repo_group': repo_group.group_id if repo_group else None,
682 682 'repo_landing_rev': landing_rev,
683 683 'enable_statistics': enable_statistics,
684 684 'enable_locking': enable_locking,
685 685 'enable_downloads': enable_downloads,
686 686 'repo_copy_permissions': copy_permissions,
687 687 }
688 688
689 689 if repo_type not in BACKENDS.keys():
690 690 raise Exception("Invalid backend type %s" % repo_type)
691 691 task = RepoModel().create(form_data=data, cur_user=owner)
692 692 from celery.result import BaseAsyncResult
693 693 task_id = None
694 694 if isinstance(task, BaseAsyncResult):
695 695 task_id = task.task_id
696 696 # no commit, it's done in RepoModel, or async via celery
697 697 return {
698 698 'msg': "Created new repository `%s`" % (repo_name,),
699 699 'success': True, # cannot return the repo data here since fork
700 700 # cann be done async
701 701 'task': task_id
702 702 }
703 703 except Exception:
704 704 log.exception(
705 705 u"Exception while trying to create the repository %s",
706 706 repo_name)
707 707 raise JSONRPCError(
708 708 'failed to create repository `%s`' % (repo_name,))
709 709
710 710
711 711 @jsonrpc_method()
712 712 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
713 713 description=Optional('')):
714 714 """
715 715 Adds an extra field to a repository.
716 716
717 717 This command can only be run using an |authtoken| with at least
718 718 write permissions to the |repo|.
719 719
720 720 :param apiuser: This is filled automatically from the |authtoken|.
721 721 :type apiuser: AuthUser
722 722 :param repoid: Set the repository name or repository id.
723 723 :type repoid: str or int
724 724 :param key: Create a unique field key for this repository.
725 725 :type key: str
726 726 :param label:
727 727 :type label: Optional(str)
728 728 :param description:
729 729 :type description: Optional(str)
730 730 """
731 731 repo = get_repo_or_error(repoid)
732 732 if not has_superadmin_permission(apiuser):
733 733 _perms = ('repository.admin',)
734 734 has_repo_permissions(apiuser, repoid, repo, _perms)
735 735
736 736 label = Optional.extract(label) or key
737 737 description = Optional.extract(description)
738 738
739 739 field = RepositoryField.get_by_key_name(key, repo)
740 740 if field:
741 741 raise JSONRPCError('Field with key '
742 742 '`%s` exists for repo `%s`' % (key, repoid))
743 743
744 744 try:
745 745 RepoModel().add_repo_field(repo, key, field_label=label,
746 746 field_desc=description)
747 747 Session().commit()
748 748 return {
749 749 'msg': "Added new repository field `%s`" % (key,),
750 750 'success': True,
751 751 }
752 752 except Exception:
753 753 log.exception("Exception occurred while trying to add field to repo")
754 754 raise JSONRPCError(
755 755 'failed to create new field for repository `%s`' % (repoid,))
756 756
757 757
758 758 @jsonrpc_method()
759 759 def remove_field_from_repo(request, apiuser, repoid, key):
760 760 """
761 761 Removes an extra field from a repository.
762 762
763 763 This command can only be run using an |authtoken| with at least
764 764 write permissions to the |repo|.
765 765
766 766 :param apiuser: This is filled automatically from the |authtoken|.
767 767 :type apiuser: AuthUser
768 768 :param repoid: Set the repository name or repository ID.
769 769 :type repoid: str or int
770 770 :param key: Set the unique field key for this repository.
771 771 :type key: str
772 772 """
773 773
774 774 repo = get_repo_or_error(repoid)
775 775 if not has_superadmin_permission(apiuser):
776 776 _perms = ('repository.admin',)
777 777 has_repo_permissions(apiuser, repoid, repo, _perms)
778 778
779 779 field = RepositoryField.get_by_key_name(key, repo)
780 780 if not field:
781 781 raise JSONRPCError('Field with key `%s` does not '
782 782 'exists for repo `%s`' % (key, repoid))
783 783
784 784 try:
785 785 RepoModel().delete_repo_field(repo, field_key=key)
786 786 Session().commit()
787 787 return {
788 788 'msg': "Deleted repository field `%s`" % (key,),
789 789 'success': True,
790 790 }
791 791 except Exception:
792 792 log.exception(
793 793 "Exception occurred while trying to delete field from repo")
794 794 raise JSONRPCError(
795 795 'failed to delete field for repository `%s`' % (repoid,))
796 796
797 797
798 798 @jsonrpc_method()
799 799 def update_repo(request, apiuser, repoid, name=Optional(None),
800 800 owner=Optional(OAttr('apiuser')),
801 801 group=Optional(None),
802 active=Optional(True),
802 803 fork_of=Optional(None),
803 804 description=Optional(''), private=Optional(False),
804 805 clone_uri=Optional(None), landing_rev=Optional('rev:tip'),
805 806 enable_statistics=Optional(False),
806 807 enable_locking=Optional(False),
807 808 enable_downloads=Optional(False),
808 809 fields=Optional('')):
809 810 """
810 811 Updates a repository with the given information.
811 812
812 813 This command can only be run using an |authtoken| with at least
813 814 write permissions to the |repo|.
814 815
815 816 :param apiuser: This is filled automatically from the |authtoken|.
816 817 :type apiuser: AuthUser
817 818 :param repoid: repository name or repository ID.
818 819 :type repoid: str or int
819 820 :param name: Update the |repo| name.
820 821 :type name: str
821 822 :param owner: Set the |repo| owner.
822 823 :type owner: str
823 824 :param group: Set the |repo| group the |repo| belongs to.
824 825 :type group: str
825 826 :param fork_of: Set the master |repo| name.
826 827 :type fork_of: str
827 828 :param description: Update the |repo| description.
828 829 :type description: str
829 830 :param private: Set the |repo| as private. (True | False)
830 831 :type private: bool
831 832 :param clone_uri: Update the |repo| clone URI.
832 833 :type clone_uri: str
833 834 :param landing_rev: Set the |repo| landing revision. Default is
834 835 ``tip``.
835 836 :type landing_rev: str
836 837 :param enable_statistics: Enable statistics on the |repo|,
837 838 (True | False).
838 839 :type enable_statistics: bool
839 840 :param enable_locking: Enable |repo| locking.
840 841 :type enable_locking: bool
841 842 :param enable_downloads: Enable downloads from the |repo|,
842 843 (True | False).
843 844 :type enable_downloads: bool
844 845 :param fields: Add extra fields to the |repo|. Use the following
845 846 example format: ``field_key=field_val,field_key2=fieldval2``.
846 847 Escape ', ' with \,
847 848 :type fields: str
848 849 """
849 850 repo = get_repo_or_error(repoid)
850 851 include_secrets = False
851 852 if has_superadmin_permission(apiuser):
852 853 include_secrets = True
853 854 else:
854 855 _perms = ('repository.admin',)
855 856 has_repo_permissions(apiuser, repoid, repo, _perms)
856 857
857 858 updates = {
858 859 # update function requires this.
859 860 'repo_name': repo.just_name
860 861 }
861 862 repo_group = group
862 863 if not isinstance(repo_group, Optional):
863 864 repo_group = get_repo_group_or_error(repo_group)
864 865 repo_group = repo_group.group_id
865 866
866 867 repo_fork_of = fork_of
867 868 if not isinstance(repo_fork_of, Optional):
868 869 repo_fork_of = get_repo_or_error(repo_fork_of)
869 870 repo_fork_of = repo_fork_of.repo_id
870 871
871 872 try:
872 873 store_update(updates, name, 'repo_name')
873 874 store_update(updates, repo_group, 'repo_group')
874 875 store_update(updates, repo_fork_of, 'fork_id')
875 876 store_update(updates, owner, 'user')
877 store_update(updates, active, 'active')
876 878 store_update(updates, description, 'repo_description')
877 879 store_update(updates, private, 'repo_private')
878 880 store_update(updates, clone_uri, 'clone_uri')
879 881 store_update(updates, landing_rev, 'repo_landing_rev')
880 882 store_update(updates, enable_statistics, 'repo_enable_statistics')
881 883 store_update(updates, enable_locking, 'repo_enable_locking')
882 884 store_update(updates, enable_downloads, 'repo_enable_downloads')
883 885
884 886 # extra fields
885 887 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
886 888 if fields:
887 889 updates.update(fields)
888 890
889 891 RepoModel().update(repo, **updates)
890 892 Session().commit()
891 893 return {
892 894 'msg': 'updated repo ID:%s %s' % (
893 895 repo.repo_id, repo.repo_name),
894 896 'repository': repo.get_api_data(
895 897 include_secrets=include_secrets)
896 898 }
897 899 except Exception:
898 900 log.exception(
899 901 u"Exception while trying to update the repository %s",
900 902 repoid)
901 903 raise JSONRPCError('failed to update repo `%s`' % repoid)
902 904
903 905
904 906 @jsonrpc_method()
905 907 def fork_repo(request, apiuser, repoid, fork_name,
906 908 owner=Optional(OAttr('apiuser')),
907 909 description=Optional(''), copy_permissions=Optional(False),
908 910 private=Optional(False), landing_rev=Optional('rev:tip')):
909 911 """
910 912 Creates a fork of the specified |repo|.
911 913
912 914 * If using |RCE| with Celery this will immediately return a success
913 915 message, even though the fork will be created asynchronously.
914 916
915 917 This command can only be run using an |authtoken| with fork
916 918 permissions on the |repo|.
917 919
918 920 :param apiuser: This is filled automatically from the |authtoken|.
919 921 :type apiuser: AuthUser
920 922 :param repoid: Set repository name or repository ID.
921 923 :type repoid: str or int
922 924 :param fork_name: Set the fork name.
923 925 :type fork_name: str
924 926 :param owner: Set the fork owner.
925 927 :type owner: str
926 928 :param description: Set the fork descripton.
927 929 :type description: str
928 930 :param copy_permissions: Copy permissions from parent |repo|. The
929 931 default is False.
930 932 :type copy_permissions: bool
931 933 :param private: Make the fork private. The default is False.
932 934 :type private: bool
933 935 :param landing_rev: Set the landing revision. The default is tip.
934 936
935 937 Example output:
936 938
937 939 .. code-block:: bash
938 940
939 941 id : <id_for_response>
940 942 api_key : "<api_key>"
941 943 args: {
942 944 "repoid" : "<reponame or repo_id>",
943 945 "fork_name": "<forkname>",
944 946 "owner": "<username or user_id = Optional(=apiuser)>",
945 947 "description": "<description>",
946 948 "copy_permissions": "<bool>",
947 949 "private": "<bool>",
948 950 "landing_rev": "<landing_rev>"
949 951 }
950 952
951 953 Example error output:
952 954
953 955 .. code-block:: bash
954 956
955 957 id : <id_given_in_input>
956 958 result: {
957 959 "msg": "Created fork of `<reponame>` as `<forkname>`",
958 960 "success": true,
959 961 "task": "<celery task id or None if done sync>"
960 962 }
961 963 error: null
962 964
963 965 """
964 966 if not has_superadmin_permission(apiuser):
965 967 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
966 968 raise JSONRPCForbidden()
967 969
968 970 repo = get_repo_or_error(repoid)
969 971 repo_name = repo.repo_name
970 972
971 973 (fork_name_cleaned,
972 974 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
973 975 fork_name)
974 976
975 977 if not has_superadmin_permission(apiuser):
976 978 # check if we have at least read permission for
977 979 # this repo that we fork !
978 980 _perms = (
979 981 'repository.admin', 'repository.write', 'repository.read')
980 982 has_repo_permissions(apiuser, repoid, repo, _perms)
981 983
982 984 if not isinstance(owner, Optional):
983 985 # forbid setting owner for non super admins
984 986 raise JSONRPCError(
985 987 'Only RhodeCode admin can specify `owner` param'
986 988 )
987 989 # check if we have a create.repo permission if not maybe the parent
988 990 # group permission
989 991 if not HasPermissionAnyApi('hg.create.repository')(user=apiuser):
990 992 if parent_group_name:
991 993 repogroupid = parent_group_name
992 994 repo_group = get_repo_group_or_error(parent_group_name)
993 995
994 996 _perms = ('group.admin',)
995 997 if not HasRepoGroupPermissionAnyApi(*_perms)(
996 998 user=apiuser, group_name=repo_group.group_name):
997 999 raise JSONRPCError(
998 1000 'repository group `%s` does not exist' % (
999 1001 repogroupid,))
1000 1002 else:
1001 1003 raise JSONRPCForbidden()
1002 1004
1003 1005 _repo = RepoModel().get_by_repo_name(fork_name)
1004 1006 if _repo:
1005 1007 type_ = 'fork' if _repo.fork else 'repo'
1006 1008 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1007 1009
1008 1010 if isinstance(owner, Optional):
1009 1011 owner = apiuser.user_id
1010 1012
1011 1013 owner = get_user_or_error(owner)
1012 1014
1013 1015 try:
1014 1016 # create structure of groups and return the last group
1015 1017 repo_group = map_groups(fork_name)
1016 1018 form_data = {
1017 1019 'repo_name': fork_name_cleaned,
1018 1020 'repo_name_full': fork_name,
1019 1021 'repo_group': repo_group.group_id if repo_group else None,
1020 1022 'repo_type': repo.repo_type,
1021 1023 'description': Optional.extract(description),
1022 1024 'private': Optional.extract(private),
1023 1025 'copy_permissions': Optional.extract(copy_permissions),
1024 1026 'landing_rev': Optional.extract(landing_rev),
1025 1027 'fork_parent_id': repo.repo_id,
1026 1028 }
1027 1029
1028 1030 task = RepoModel().create_fork(form_data, cur_user=owner)
1029 1031 # no commit, it's done in RepoModel, or async via celery
1030 1032 from celery.result import BaseAsyncResult
1031 1033 task_id = None
1032 1034 if isinstance(task, BaseAsyncResult):
1033 1035 task_id = task.task_id
1034 1036 return {
1035 1037 'msg': 'Created fork of `%s` as `%s`' % (
1036 1038 repo.repo_name, fork_name),
1037 1039 'success': True, # cannot return the repo data here since fork
1038 1040 # can be done async
1039 1041 'task': task_id
1040 1042 }
1041 1043 except Exception:
1042 1044 log.exception("Exception occurred while trying to fork a repo")
1043 1045 raise JSONRPCError(
1044 1046 'failed to fork repository `%s` as `%s`' % (
1045 1047 repo_name, fork_name))
1046 1048
1047 1049
1048 1050 @jsonrpc_method()
1049 1051 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1050 1052 """
1051 1053 Deletes a repository.
1052 1054
1053 1055 * When the `forks` parameter is set it's possible to detach or delete
1054 1056 forks of deleted repository.
1055 1057
1056 1058 This command can only be run using an |authtoken| with admin
1057 1059 permissions on the |repo|.
1058 1060
1059 1061 :param apiuser: This is filled automatically from the |authtoken|.
1060 1062 :type apiuser: AuthUser
1061 1063 :param repoid: Set the repository name or repository ID.
1062 1064 :type repoid: str or int
1063 1065 :param forks: Set to `detach` or `delete` forks from the |repo|.
1064 1066 :type forks: Optional(str)
1065 1067
1066 1068 Example error output:
1067 1069
1068 1070 .. code-block:: bash
1069 1071
1070 1072 id : <id_given_in_input>
1071 1073 result: {
1072 1074 "msg": "Deleted repository `<reponame>`",
1073 1075 "success": true
1074 1076 }
1075 1077 error: null
1076 1078 """
1077 1079
1078 1080 repo = get_repo_or_error(repoid)
1079 1081 if not has_superadmin_permission(apiuser):
1080 1082 _perms = ('repository.admin',)
1081 1083 has_repo_permissions(apiuser, repoid, repo, _perms)
1082 1084
1083 1085 try:
1084 1086 handle_forks = Optional.extract(forks)
1085 1087 _forks_msg = ''
1086 1088 _forks = [f for f in repo.forks]
1087 1089 if handle_forks == 'detach':
1088 1090 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1089 1091 elif handle_forks == 'delete':
1090 1092 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1091 1093 elif _forks:
1092 1094 raise JSONRPCError(
1093 1095 'Cannot delete `%s` it still contains attached forks' %
1094 1096 (repo.repo_name,)
1095 1097 )
1096 1098
1097 1099 RepoModel().delete(repo, forks=forks)
1098 1100 Session().commit()
1099 1101 return {
1100 1102 'msg': 'Deleted repository `%s`%s' % (
1101 1103 repo.repo_name, _forks_msg),
1102 1104 'success': True
1103 1105 }
1104 1106 except Exception:
1105 1107 log.exception("Exception occurred while trying to delete repo")
1106 1108 raise JSONRPCError(
1107 1109 'failed to delete repository `%s`' % (repo.repo_name,)
1108 1110 )
1109 1111
1110 1112
1111 1113 #TODO: marcink, change name ?
1112 1114 @jsonrpc_method()
1113 1115 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1114 1116 """
1115 1117 Invalidates the cache for the specified repository.
1116 1118
1117 1119 This command can only be run using an |authtoken| with admin rights to
1118 1120 the specified repository.
1119 1121
1120 1122 This command takes the following options:
1121 1123
1122 1124 :param apiuser: This is filled automatically from |authtoken|.
1123 1125 :type apiuser: AuthUser
1124 1126 :param repoid: Sets the repository name or repository ID.
1125 1127 :type repoid: str or int
1126 1128 :param delete_keys: This deletes the invalidated keys instead of
1127 1129 just flagging them.
1128 1130 :type delete_keys: Optional(``True`` | ``False``)
1129 1131
1130 1132 Example output:
1131 1133
1132 1134 .. code-block:: bash
1133 1135
1134 1136 id : <id_given_in_input>
1135 1137 result : {
1136 1138 'msg': Cache for repository `<repository name>` was invalidated,
1137 1139 'repository': <repository name>
1138 1140 }
1139 1141 error : null
1140 1142
1141 1143 Example error output:
1142 1144
1143 1145 .. code-block:: bash
1144 1146
1145 1147 id : <id_given_in_input>
1146 1148 result : null
1147 1149 error : {
1148 1150 'Error occurred during cache invalidation action'
1149 1151 }
1150 1152
1151 1153 """
1152 1154
1153 1155 repo = get_repo_or_error(repoid)
1154 1156 if not has_superadmin_permission(apiuser):
1155 1157 _perms = ('repository.admin', 'repository.write',)
1156 1158 has_repo_permissions(apiuser, repoid, repo, _perms)
1157 1159
1158 1160 delete = Optional.extract(delete_keys)
1159 1161 try:
1160 1162 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1161 1163 return {
1162 1164 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1163 1165 'repository': repo.repo_name
1164 1166 }
1165 1167 except Exception:
1166 1168 log.exception(
1167 1169 "Exception occurred while trying to invalidate repo cache")
1168 1170 raise JSONRPCError(
1169 1171 'Error occurred during cache invalidation action'
1170 1172 )
1171 1173
1172 1174
1173 1175 #TODO: marcink, change name ?
1174 1176 @jsonrpc_method()
1175 1177 def lock(request, apiuser, repoid, locked=Optional(None),
1176 1178 userid=Optional(OAttr('apiuser'))):
1177 1179 """
1178 1180 Sets the lock state of the specified |repo| by the given user.
1179 1181 From more information, see :ref:`repo-locking`.
1180 1182
1181 1183 * If the ``userid`` option is not set, the repository is locked to the
1182 1184 user who called the method.
1183 1185 * If the ``locked`` parameter is not set, the current lock state of the
1184 1186 repository is displayed.
1185 1187
1186 1188 This command can only be run using an |authtoken| with admin rights to
1187 1189 the specified repository.
1188 1190
1189 1191 This command takes the following options:
1190 1192
1191 1193 :param apiuser: This is filled automatically from the |authtoken|.
1192 1194 :type apiuser: AuthUser
1193 1195 :param repoid: Sets the repository name or repository ID.
1194 1196 :type repoid: str or int
1195 1197 :param locked: Sets the lock state.
1196 1198 :type locked: Optional(``True`` | ``False``)
1197 1199 :param userid: Set the repository lock to this user.
1198 1200 :type userid: Optional(str or int)
1199 1201
1200 1202 Example error output:
1201 1203
1202 1204 .. code-block:: bash
1203 1205
1204 1206 id : <id_given_in_input>
1205 1207 result : {
1206 1208 'repo': '<reponame>',
1207 1209 'locked': <bool: lock state>,
1208 1210 'locked_since': <int: lock timestamp>,
1209 1211 'locked_by': <username of person who made the lock>,
1210 1212 'lock_reason': <str: reason for locking>,
1211 1213 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1212 1214 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1213 1215 or
1214 1216 'msg': 'Repo `<repository name>` not locked.'
1215 1217 or
1216 1218 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1217 1219 }
1218 1220 error : null
1219 1221
1220 1222 Example error output:
1221 1223
1222 1224 .. code-block:: bash
1223 1225
1224 1226 id : <id_given_in_input>
1225 1227 result : null
1226 1228 error : {
1227 1229 'Error occurred locking repository `<reponame>`
1228 1230 }
1229 1231 """
1230 1232
1231 1233 repo = get_repo_or_error(repoid)
1232 1234 if not has_superadmin_permission(apiuser):
1233 1235 # check if we have at least write permission for this repo !
1234 1236 _perms = ('repository.admin', 'repository.write',)
1235 1237 has_repo_permissions(apiuser, repoid, repo, _perms)
1236 1238
1237 1239 # make sure normal user does not pass someone else userid,
1238 1240 # he is not allowed to do that
1239 1241 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1240 1242 raise JSONRPCError('userid is not the same as your user')
1241 1243
1242 1244 if isinstance(userid, Optional):
1243 1245 userid = apiuser.user_id
1244 1246
1245 1247 user = get_user_or_error(userid)
1246 1248
1247 1249 if isinstance(locked, Optional):
1248 1250 lockobj = repo.locked
1249 1251
1250 1252 if lockobj[0] is None:
1251 1253 _d = {
1252 1254 'repo': repo.repo_name,
1253 1255 'locked': False,
1254 1256 'locked_since': None,
1255 1257 'locked_by': None,
1256 1258 'lock_reason': None,
1257 1259 'lock_state_changed': False,
1258 1260 'msg': 'Repo `%s` not locked.' % repo.repo_name
1259 1261 }
1260 1262 return _d
1261 1263 else:
1262 1264 _user_id, _time, _reason = lockobj
1263 1265 lock_user = get_user_or_error(userid)
1264 1266 _d = {
1265 1267 'repo': repo.repo_name,
1266 1268 'locked': True,
1267 1269 'locked_since': _time,
1268 1270 'locked_by': lock_user.username,
1269 1271 'lock_reason': _reason,
1270 1272 'lock_state_changed': False,
1271 1273 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1272 1274 % (repo.repo_name, lock_user.username,
1273 1275 json.dumps(time_to_datetime(_time))))
1274 1276 }
1275 1277 return _d
1276 1278
1277 1279 # force locked state through a flag
1278 1280 else:
1279 1281 locked = str2bool(locked)
1280 1282 lock_reason = Repository.LOCK_API
1281 1283 try:
1282 1284 if locked:
1283 1285 lock_time = time.time()
1284 1286 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1285 1287 else:
1286 1288 lock_time = None
1287 1289 Repository.unlock(repo)
1288 1290 _d = {
1289 1291 'repo': repo.repo_name,
1290 1292 'locked': locked,
1291 1293 'locked_since': lock_time,
1292 1294 'locked_by': user.username,
1293 1295 'lock_reason': lock_reason,
1294 1296 'lock_state_changed': True,
1295 1297 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1296 1298 % (user.username, repo.repo_name, locked))
1297 1299 }
1298 1300 return _d
1299 1301 except Exception:
1300 1302 log.exception(
1301 1303 "Exception occurred while trying to lock repository")
1302 1304 raise JSONRPCError(
1303 1305 'Error occurred locking repository `%s`' % repo.repo_name
1304 1306 )
1305 1307
1306 1308
1307 1309 @jsonrpc_method()
1308 1310 def comment_commit(
1309 1311 request, apiuser, repoid, commit_id, message,
1310 1312 userid=Optional(OAttr('apiuser')), status=Optional(None)):
1311 1313 """
1312 1314 Set a commit comment, and optionally change the status of the commit.
1313 1315 This command can be executed only using api_key belonging to user
1314 1316 with admin rights, or repository administrator.
1315 1317
1316 1318 :param apiuser: This is filled automatically from the |authtoken|.
1317 1319 :type apiuser: AuthUser
1318 1320 :param repoid: Set the repository name or repository ID.
1319 1321 :type repoid: str or int
1320 1322 :param commit_id: Specify the commit_id for which to set a comment.
1321 1323 :type commit_id: str
1322 1324 :param message: The comment text.
1323 1325 :type message: str
1324 1326 :param userid: Set the user name of the comment creator.
1325 1327 :type userid: Optional(str or int)
1326 1328 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
1327 1329 'under_review'
1328 1330 :type status: str
1329 1331
1330 1332 Example error output:
1331 1333
1332 1334 .. code-block:: json
1333 1335
1334 1336 {
1335 1337 "id" : <id_given_in_input>,
1336 1338 "result" : {
1337 1339 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1338 1340 "status_change": null or <status>,
1339 1341 "success": true
1340 1342 },
1341 1343 "error" : null
1342 1344 }
1343 1345
1344 1346 """
1345 1347 repo = get_repo_or_error(repoid)
1346 1348 if not has_superadmin_permission(apiuser):
1347 1349 _perms = ('repository.admin',)
1348 1350 has_repo_permissions(apiuser, repoid, repo, _perms)
1349 1351
1350 1352 if isinstance(userid, Optional):
1351 1353 userid = apiuser.user_id
1352 1354
1353 1355 user = get_user_or_error(userid)
1354 1356 status = Optional.extract(status)
1355 1357
1356 1358 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1357 1359 if status and status not in allowed_statuses:
1358 1360 raise JSONRPCError('Bad status, must be on '
1359 1361 'of %s got %s' % (allowed_statuses, status,))
1360 1362
1361 1363 try:
1362 1364 rc_config = SettingsModel().get_all_settings()
1363 1365 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1364 1366
1365 1367 comm = ChangesetCommentsModel().create(
1366 1368 message, repo, user, revision=commit_id, status_change=status,
1367 1369 renderer=renderer)
1368 1370 if status:
1369 1371 # also do a status change
1370 1372 try:
1371 1373 ChangesetStatusModel().set_status(
1372 1374 repo, status, user, comm, revision=commit_id,
1373 1375 dont_allow_on_closed_pull_request=True
1374 1376 )
1375 1377 except StatusChangeOnClosedPullRequestError:
1376 1378 log.exception(
1377 1379 "Exception occurred while trying to change repo commit status")
1378 1380 msg = ('Changing status on a changeset associated with '
1379 1381 'a closed pull request is not allowed')
1380 1382 raise JSONRPCError(msg)
1381 1383
1382 1384 Session().commit()
1383 1385 return {
1384 1386 'msg': (
1385 1387 'Commented on commit `%s` for repository `%s`' % (
1386 1388 comm.revision, repo.repo_name)),
1387 1389 'status_change': status,
1388 1390 'success': True,
1389 1391 }
1390 1392 except JSONRPCError:
1391 1393 # catch any inside errors, and re-raise them to prevent from
1392 1394 # below global catch to silence them
1393 1395 raise
1394 1396 except Exception:
1395 1397 log.exception("Exception occurred while trying to comment on commit")
1396 1398 raise JSONRPCError(
1397 1399 'failed to set comment on repository `%s`' % (repo.repo_name,)
1398 1400 )
1399 1401
1400 1402
1401 1403 @jsonrpc_method()
1402 1404 def grant_user_permission(request, apiuser, repoid, userid, perm):
1403 1405 """
1404 1406 Grant permissions for the specified user on the given repository,
1405 1407 or update existing permissions if found.
1406 1408
1407 1409 This command can only be run using an |authtoken| with admin
1408 1410 permissions on the |repo|.
1409 1411
1410 1412 :param apiuser: This is filled automatically from the |authtoken|.
1411 1413 :type apiuser: AuthUser
1412 1414 :param repoid: Set the repository name or repository ID.
1413 1415 :type repoid: str or int
1414 1416 :param userid: Set the user name.
1415 1417 :type userid: str
1416 1418 :param perm: Set the user permissions, using the following format
1417 1419 ``(repository.(none|read|write|admin))``
1418 1420 :type perm: str
1419 1421
1420 1422 Example output:
1421 1423
1422 1424 .. code-block:: bash
1423 1425
1424 1426 id : <id_given_in_input>
1425 1427 result: {
1426 1428 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1427 1429 "success": true
1428 1430 }
1429 1431 error: null
1430 1432 """
1431 1433
1432 1434 repo = get_repo_or_error(repoid)
1433 1435 user = get_user_or_error(userid)
1434 1436 perm = get_perm_or_error(perm)
1435 1437 if not has_superadmin_permission(apiuser):
1436 1438 _perms = ('repository.admin',)
1437 1439 has_repo_permissions(apiuser, repoid, repo, _perms)
1438 1440
1439 1441 try:
1440 1442
1441 1443 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1442 1444
1443 1445 Session().commit()
1444 1446 return {
1445 1447 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1446 1448 perm.permission_name, user.username, repo.repo_name
1447 1449 ),
1448 1450 'success': True
1449 1451 }
1450 1452 except Exception:
1451 1453 log.exception(
1452 1454 "Exception occurred while trying edit permissions for repo")
1453 1455 raise JSONRPCError(
1454 1456 'failed to edit permission for user: `%s` in repo: `%s`' % (
1455 1457 userid, repoid
1456 1458 )
1457 1459 )
1458 1460
1459 1461
1460 1462 @jsonrpc_method()
1461 1463 def revoke_user_permission(request, apiuser, repoid, userid):
1462 1464 """
1463 1465 Revoke permission for a user on the specified repository.
1464 1466
1465 1467 This command can only be run using an |authtoken| with admin
1466 1468 permissions on the |repo|.
1467 1469
1468 1470 :param apiuser: This is filled automatically from the |authtoken|.
1469 1471 :type apiuser: AuthUser
1470 1472 :param repoid: Set the repository name or repository ID.
1471 1473 :type repoid: str or int
1472 1474 :param userid: Set the user name of revoked user.
1473 1475 :type userid: str or int
1474 1476
1475 1477 Example error output:
1476 1478
1477 1479 .. code-block:: bash
1478 1480
1479 1481 id : <id_given_in_input>
1480 1482 result: {
1481 1483 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1482 1484 "success": true
1483 1485 }
1484 1486 error: null
1485 1487 """
1486 1488
1487 1489 repo = get_repo_or_error(repoid)
1488 1490 user = get_user_or_error(userid)
1489 1491 if not has_superadmin_permission(apiuser):
1490 1492 _perms = ('repository.admin',)
1491 1493 has_repo_permissions(apiuser, repoid, repo, _perms)
1492 1494
1493 1495 try:
1494 1496 RepoModel().revoke_user_permission(repo=repo, user=user)
1495 1497 Session().commit()
1496 1498 return {
1497 1499 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1498 1500 user.username, repo.repo_name
1499 1501 ),
1500 1502 'success': True
1501 1503 }
1502 1504 except Exception:
1503 1505 log.exception(
1504 1506 "Exception occurred while trying revoke permissions to repo")
1505 1507 raise JSONRPCError(
1506 1508 'failed to edit permission for user: `%s` in repo: `%s`' % (
1507 1509 userid, repoid
1508 1510 )
1509 1511 )
1510 1512
1511 1513
1512 1514 @jsonrpc_method()
1513 1515 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1514 1516 """
1515 1517 Grant permission for a user group on the specified repository,
1516 1518 or update existing permissions.
1517 1519
1518 1520 This command can only be run using an |authtoken| with admin
1519 1521 permissions on the |repo|.
1520 1522
1521 1523 :param apiuser: This is filled automatically from the |authtoken|.
1522 1524 :type apiuser: AuthUser
1523 1525 :param repoid: Set the repository name or repository ID.
1524 1526 :type repoid: str or int
1525 1527 :param usergroupid: Specify the ID of the user group.
1526 1528 :type usergroupid: str or int
1527 1529 :param perm: Set the user group permissions using the following
1528 1530 format: (repository.(none|read|write|admin))
1529 1531 :type perm: str
1530 1532
1531 1533 Example output:
1532 1534
1533 1535 .. code-block:: bash
1534 1536
1535 1537 id : <id_given_in_input>
1536 1538 result : {
1537 1539 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1538 1540 "success": true
1539 1541
1540 1542 }
1541 1543 error : null
1542 1544
1543 1545 Example error output:
1544 1546
1545 1547 .. code-block:: bash
1546 1548
1547 1549 id : <id_given_in_input>
1548 1550 result : null
1549 1551 error : {
1550 1552 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1551 1553 }
1552 1554
1553 1555 """
1554 1556
1555 1557 repo = get_repo_or_error(repoid)
1556 1558 perm = get_perm_or_error(perm)
1557 1559 if not has_superadmin_permission(apiuser):
1558 1560 _perms = ('repository.admin',)
1559 1561 has_repo_permissions(apiuser, repoid, repo, _perms)
1560 1562
1561 1563 user_group = get_user_group_or_error(usergroupid)
1562 1564 if not has_superadmin_permission(apiuser):
1563 1565 # check if we have at least read permission for this user group !
1564 1566 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1565 1567 if not HasUserGroupPermissionAnyApi(*_perms)(
1566 1568 user=apiuser, user_group_name=user_group.users_group_name):
1567 1569 raise JSONRPCError(
1568 1570 'user group `%s` does not exist' % (usergroupid,))
1569 1571
1570 1572 try:
1571 1573 RepoModel().grant_user_group_permission(
1572 1574 repo=repo, group_name=user_group, perm=perm)
1573 1575
1574 1576 Session().commit()
1575 1577 return {
1576 1578 'msg': 'Granted perm: `%s` for user group: `%s` in '
1577 1579 'repo: `%s`' % (
1578 1580 perm.permission_name, user_group.users_group_name,
1579 1581 repo.repo_name
1580 1582 ),
1581 1583 'success': True
1582 1584 }
1583 1585 except Exception:
1584 1586 log.exception(
1585 1587 "Exception occurred while trying change permission on repo")
1586 1588 raise JSONRPCError(
1587 1589 'failed to edit permission for user group: `%s` in '
1588 1590 'repo: `%s`' % (
1589 1591 usergroupid, repo.repo_name
1590 1592 )
1591 1593 )
1592 1594
1593 1595
1594 1596 @jsonrpc_method()
1595 1597 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1596 1598 """
1597 1599 Revoke the permissions of a user group on a given repository.
1598 1600
1599 1601 This command can only be run using an |authtoken| with admin
1600 1602 permissions on the |repo|.
1601 1603
1602 1604 :param apiuser: This is filled automatically from the |authtoken|.
1603 1605 :type apiuser: AuthUser
1604 1606 :param repoid: Set the repository name or repository ID.
1605 1607 :type repoid: str or int
1606 1608 :param usergroupid: Specify the user group ID.
1607 1609 :type usergroupid: str or int
1608 1610
1609 1611 Example output:
1610 1612
1611 1613 .. code-block:: bash
1612 1614
1613 1615 id : <id_given_in_input>
1614 1616 result: {
1615 1617 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1616 1618 "success": true
1617 1619 }
1618 1620 error: null
1619 1621 """
1620 1622
1621 1623 repo = get_repo_or_error(repoid)
1622 1624 if not has_superadmin_permission(apiuser):
1623 1625 _perms = ('repository.admin',)
1624 1626 has_repo_permissions(apiuser, repoid, repo, _perms)
1625 1627
1626 1628 user_group = get_user_group_or_error(usergroupid)
1627 1629 if not has_superadmin_permission(apiuser):
1628 1630 # check if we have at least read permission for this user group !
1629 1631 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1630 1632 if not HasUserGroupPermissionAnyApi(*_perms)(
1631 1633 user=apiuser, user_group_name=user_group.users_group_name):
1632 1634 raise JSONRPCError(
1633 1635 'user group `%s` does not exist' % (usergroupid,))
1634 1636
1635 1637 try:
1636 1638 RepoModel().revoke_user_group_permission(
1637 1639 repo=repo, group_name=user_group)
1638 1640
1639 1641 Session().commit()
1640 1642 return {
1641 1643 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1642 1644 user_group.users_group_name, repo.repo_name
1643 1645 ),
1644 1646 'success': True
1645 1647 }
1646 1648 except Exception:
1647 1649 log.exception("Exception occurred while trying revoke "
1648 1650 "user group permission on repo")
1649 1651 raise JSONRPCError(
1650 1652 'failed to edit permission for user group: `%s` in '
1651 1653 'repo: `%s`' % (
1652 1654 user_group.users_group_name, repo.repo_name
1653 1655 )
1654 1656 )
1655 1657
1656 1658
1657 1659 @jsonrpc_method()
1658 1660 def pull(request, apiuser, repoid):
1659 1661 """
1660 1662 Triggers a pull on the given repository from a remote location. You
1661 1663 can use this to keep remote repositories up-to-date.
1662 1664
1663 1665 This command can only be run using an |authtoken| with admin
1664 1666 rights to the specified repository. For more information,
1665 1667 see :ref:`config-token-ref`.
1666 1668
1667 1669 This command takes the following options:
1668 1670
1669 1671 :param apiuser: This is filled automatically from the |authtoken|.
1670 1672 :type apiuser: AuthUser
1671 1673 :param repoid: The repository name or repository ID.
1672 1674 :type repoid: str or int
1673 1675
1674 1676 Example output:
1675 1677
1676 1678 .. code-block:: bash
1677 1679
1678 1680 id : <id_given_in_input>
1679 1681 result : {
1680 1682 "msg": "Pulled from `<repository name>`"
1681 1683 "repository": "<repository name>"
1682 1684 }
1683 1685 error : null
1684 1686
1685 1687 Example error output:
1686 1688
1687 1689 .. code-block:: bash
1688 1690
1689 1691 id : <id_given_in_input>
1690 1692 result : null
1691 1693 error : {
1692 1694 "Unable to pull changes from `<reponame>`"
1693 1695 }
1694 1696
1695 1697 """
1696 1698
1697 1699 repo = get_repo_or_error(repoid)
1698 1700 if not has_superadmin_permission(apiuser):
1699 1701 _perms = ('repository.admin',)
1700 1702 has_repo_permissions(apiuser, repoid, repo, _perms)
1701 1703
1702 1704 try:
1703 1705 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1704 1706 return {
1705 1707 'msg': 'Pulled from `%s`' % repo.repo_name,
1706 1708 'repository': repo.repo_name
1707 1709 }
1708 1710 except Exception:
1709 1711 log.exception("Exception occurred while trying to "
1710 1712 "pull changes from remote location")
1711 1713 raise JSONRPCError(
1712 1714 'Unable to pull changes from `%s`' % repo.repo_name
1713 1715 )
1714 1716
1715 1717
1716 1718 @jsonrpc_method()
1717 1719 def strip(request, apiuser, repoid, revision, branch):
1718 1720 """
1719 1721 Strips the given revision from the specified repository.
1720 1722
1721 1723 * This will remove the revision and all of its decendants.
1722 1724
1723 1725 This command can only be run using an |authtoken| with admin rights to
1724 1726 the specified repository.
1725 1727
1726 1728 This command takes the following options:
1727 1729
1728 1730 :param apiuser: This is filled automatically from the |authtoken|.
1729 1731 :type apiuser: AuthUser
1730 1732 :param repoid: The repository name or repository ID.
1731 1733 :type repoid: str or int
1732 1734 :param revision: The revision you wish to strip.
1733 1735 :type revision: str
1734 1736 :param branch: The branch from which to strip the revision.
1735 1737 :type branch: str
1736 1738
1737 1739 Example output:
1738 1740
1739 1741 .. code-block:: bash
1740 1742
1741 1743 id : <id_given_in_input>
1742 1744 result : {
1743 1745 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1744 1746 "repository": "<repository name>"
1745 1747 }
1746 1748 error : null
1747 1749
1748 1750 Example error output:
1749 1751
1750 1752 .. code-block:: bash
1751 1753
1752 1754 id : <id_given_in_input>
1753 1755 result : null
1754 1756 error : {
1755 1757 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1756 1758 }
1757 1759
1758 1760 """
1759 1761
1760 1762 repo = get_repo_or_error(repoid)
1761 1763 if not has_superadmin_permission(apiuser):
1762 1764 _perms = ('repository.admin',)
1763 1765 has_repo_permissions(apiuser, repoid, repo, _perms)
1764 1766
1765 1767 try:
1766 1768 ScmModel().strip(repo, revision, branch)
1767 1769 return {
1768 1770 'msg': 'Stripped commit %s from repo `%s`' % (
1769 1771 revision, repo.repo_name),
1770 1772 'repository': repo.repo_name
1771 1773 }
1772 1774 except Exception:
1773 1775 log.exception("Exception while trying to strip")
1774 1776 raise JSONRPCError(
1775 1777 'Unable to strip commit %s from repo `%s`' % (
1776 1778 revision, repo.repo_name)
1777 1779 )
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,511 +1,517 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2016 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 """
23 23 user group model for RhodeCode
24 24 """
25 25
26 26
27 27 import logging
28 28 import traceback
29 29
30 30 from rhodecode.lib.utils2 import safe_str
31 31 from rhodecode.model import BaseModel
32 32 from rhodecode.model.db import UserGroupMember, UserGroup,\
33 33 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
34 34 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm
35 35 from rhodecode.lib.exceptions import UserGroupAssignedException,\
36 36 RepoGroupAssignmentError
37 37 from rhodecode.lib.utils2 import get_current_rhodecode_user, action_logger_generic
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class UserGroupModel(BaseModel):
43 43
44 44 cls = UserGroup
45 45
46 46 def _get_user_group(self, user_group):
47 47 return self._get_instance(UserGroup, user_group,
48 48 callback=UserGroup.get_by_group_name)
49 49
50 50 def _create_default_perms(self, user_group):
51 51 # create default permission
52 52 default_perm = 'usergroup.read'
53 53 def_user = User.get_default_user()
54 54 for p in def_user.user_perms:
55 55 if p.permission.permission_name.startswith('usergroup.'):
56 56 default_perm = p.permission.permission_name
57 57 break
58 58
59 59 user_group_to_perm = UserUserGroupToPerm()
60 60 user_group_to_perm.permission = Permission.get_by_key(default_perm)
61 61
62 62 user_group_to_perm.user_group = user_group
63 63 user_group_to_perm.user_id = def_user.user_id
64 64 return user_group_to_perm
65 65
66 66 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
67 67 perm_deletions=None, check_perms=True, cur_user=None):
68 68 from rhodecode.lib.auth import HasUserGroupPermissionAny
69 69 if not perm_additions:
70 70 perm_additions = []
71 71 if not perm_updates:
72 72 perm_updates = []
73 73 if not perm_deletions:
74 74 perm_deletions = []
75 75
76 76 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
77 77
78 78 # update permissions
79 79 for member_id, perm, member_type in perm_updates:
80 80 member_id = int(member_id)
81 81 if member_type == 'user':
82 82 # this updates existing one
83 83 self.grant_user_permission(
84 84 user_group=user_group, user=member_id, perm=perm
85 85 )
86 86 else:
87 87 # check if we have permissions to alter this usergroup
88 88 member_name = UserGroup.get(member_id).users_group_name
89 89 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
90 90 self.grant_user_group_permission(
91 91 target_user_group=user_group, user_group=member_id, perm=perm
92 92 )
93 93
94 94 # set new permissions
95 95 for member_id, perm, member_type in perm_additions:
96 96 member_id = int(member_id)
97 97 if member_type == 'user':
98 98 self.grant_user_permission(
99 99 user_group=user_group, user=member_id, perm=perm
100 100 )
101 101 else:
102 102 # check if we have permissions to alter this usergroup
103 103 member_name = UserGroup.get(member_id).users_group_name
104 104 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
105 105 self.grant_user_group_permission(
106 106 target_user_group=user_group, user_group=member_id, perm=perm
107 107 )
108 108
109 109 # delete permissions
110 110 for member_id, perm, member_type in perm_deletions:
111 111 member_id = int(member_id)
112 112 if member_type == 'user':
113 113 self.revoke_user_permission(user_group=user_group, user=member_id)
114 114 else:
115 115 #check if we have permissions to alter this usergroup
116 116 member_name = UserGroup.get(member_id).users_group_name
117 117 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
118 118 self.revoke_user_group_permission(
119 119 target_user_group=user_group, user_group=member_id
120 120 )
121 121
122 122 def get(self, user_group_id, cache=False):
123 123 return UserGroup.get(user_group_id)
124 124
125 125 def get_group(self, user_group):
126 126 return self._get_user_group(user_group)
127 127
128 128 def get_by_name(self, name, cache=False, case_insensitive=False):
129 129 return UserGroup.get_by_group_name(name, cache, case_insensitive)
130 130
131 131 def create(self, name, description, owner, active=True, group_data=None):
132 132 try:
133 133 new_user_group = UserGroup()
134 134 new_user_group.user = self._get_user(owner)
135 135 new_user_group.users_group_name = name
136 136 new_user_group.user_group_description = description
137 137 new_user_group.users_group_active = active
138 138 if group_data:
139 139 new_user_group.group_data = group_data
140 140 self.sa.add(new_user_group)
141 141 perm_obj = self._create_default_perms(new_user_group)
142 142 self.sa.add(perm_obj)
143 143
144 144 self.grant_user_permission(user_group=new_user_group,
145 145 user=owner, perm='usergroup.admin')
146 146
147 147 return new_user_group
148 148 except Exception:
149 149 log.error(traceback.format_exc())
150 150 raise
151 151
152 152 def _get_memberships_for_user_ids(self, user_group, user_id_list):
153 153 members = []
154 154 for user_id in user_id_list:
155 155 member = self._get_membership(user_group.users_group_id, user_id)
156 156 members.append(member)
157 157 return members
158 158
159 159 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
160 160 current_members = user_group.members or []
161 161 current_members_ids = [m.user.user_id for m in current_members]
162 162
163 163 added_members = [
164 164 user_id for user_id in user_id_list
165 165 if user_id not in current_members_ids]
166 166 if user_id_list == []:
167 167 # all members were deleted
168 168 deleted_members = current_members_ids
169 169 else:
170 170 deleted_members = [
171 171 user_id for user_id in current_members_ids
172 172 if user_id not in user_id_list]
173 173
174 174 return (added_members, deleted_members)
175 175
176 176 def _set_users_as_members(self, user_group, user_ids):
177 177 user_group.members = []
178 178 self.sa.flush()
179 179 members = self._get_memberships_for_user_ids(
180 180 user_group, user_ids)
181 181 user_group.members = members
182 182 self.sa.add(user_group)
183 183
184 184 def _update_members_from_user_ids(self, user_group, user_ids):
185 185 added, removed = self._get_added_and_removed_user_ids(
186 186 user_group, user_ids)
187 187 self._set_users_as_members(user_group, user_ids)
188 188 self._log_user_changes('added to', user_group, added)
189 189 self._log_user_changes('removed from', user_group, removed)
190 190
191 191 def _clean_members_data(self, members_data):
192 192 # TODO: anderson: this should be in the form validation but I couldn't
193 193 # make it work there as it conflicts with the other validator
194 194 if not members_data:
195 195 members_data = []
196 196
197 197 if isinstance(members_data, basestring):
198 198 new_members = [members_data]
199 199 else:
200 200 new_members = members_data
201 201
202 202 new_members = [int(uid) for uid in new_members]
203 203 return new_members
204 204
205 205 def update(self, user_group, form_data):
206 206 user_group = self._get_user_group(user_group)
207 207 if 'users_group_name' in form_data:
208 208 user_group.users_group_name = form_data['users_group_name']
209 209 if 'users_group_active' in form_data:
210 210 user_group.users_group_active = form_data['users_group_active']
211 211 if 'user_group_description' in form_data:
212 212 user_group.user_group_description = form_data[
213 213 'user_group_description']
214 214
215 215 # handle owner change
216 216 if 'user' in form_data:
217 217 owner = form_data['user']
218 218 if isinstance(owner, basestring):
219 user_group.user = User.get_by_username(form_data['user'])
219 owner = User.get_by_username(form_data['user'])
220
221 if not isinstance(owner, User):
222 raise ValueError(
223 'invalid owner for user group: %s' % form_data['user'])
224
225 user_group.user = owner
220 226
221 227 if 'users_group_members' in form_data:
222 228 members_id_list = self._clean_members_data(
223 229 form_data['users_group_members'])
224 230 self._update_members_from_user_ids(user_group, members_id_list)
225 231
226 232 self.sa.add(user_group)
227 233
228 234 def delete(self, user_group, force=False):
229 235 """
230 236 Deletes repository group, unless force flag is used
231 237 raises exception if there are members in that group, else deletes
232 238 group and users
233 239
234 240 :param user_group:
235 241 :param force:
236 242 """
237 243 user_group = self._get_user_group(user_group)
238 244 try:
239 245 # check if this group is not assigned to repo
240 246 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
241 247 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
242 248 # check if this group is not assigned to repo
243 249 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
244 250 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
245 251
246 252 if (assigned_to_repo or assigned_to_repo_group) and not force:
247 253 assigned = ','.join(map(safe_str,
248 254 assigned_to_repo+assigned_to_repo_group))
249 255
250 256 raise UserGroupAssignedException(
251 257 'UserGroup assigned to %s' % (assigned,))
252 258 self.sa.delete(user_group)
253 259 except Exception:
254 260 log.error(traceback.format_exc())
255 261 raise
256 262
257 263 def _log_user_changes(self, action, user_group, user_or_users):
258 264 users = user_or_users
259 265 if not isinstance(users, (list, tuple)):
260 266 users = [users]
261 267 rhodecode_user = get_current_rhodecode_user()
262 268 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
263 269 group_name = user_group.users_group_name
264 270
265 271 for user_or_user_id in users:
266 272 user = self._get_user(user_or_user_id)
267 273 log_text = 'User {user} {action} {group}'.format(
268 274 action=action, user=user.username, group=group_name)
269 275 log.info('Logging action: {0} by {1} ip:{2}'.format(
270 276 log_text, rhodecode_user, ipaddr))
271 277
272 278 def _find_user_in_group(self, user, user_group):
273 279 user_group_member = None
274 280 for m in user_group.members:
275 281 if m.user_id == user.user_id:
276 282 # Found this user's membership row
277 283 user_group_member = m
278 284 break
279 285
280 286 return user_group_member
281 287
282 288 def _get_membership(self, user_group_id, user_id):
283 289 user_group_member = UserGroupMember(user_group_id, user_id)
284 290 return user_group_member
285 291
286 292 def add_user_to_group(self, user_group, user):
287 293 user_group = self._get_user_group(user_group)
288 294 user = self._get_user(user)
289 295 user_member = self._find_user_in_group(user, user_group)
290 296 if user_member:
291 297 # user already in the group, skip
292 298 return True
293 299
294 300 member = self._get_membership(
295 301 user_group.users_group_id, user.user_id)
296 302 user_group.members.append(member)
297 303
298 304 try:
299 305 self.sa.add(member)
300 306 except Exception:
301 307 # what could go wrong here?
302 308 log.error(traceback.format_exc())
303 309 raise
304 310
305 311 self._log_user_changes('added to', user_group, user)
306 312 return member
307 313
308 314 def remove_user_from_group(self, user_group, user):
309 315 user_group = self._get_user_group(user_group)
310 316 user = self._get_user(user)
311 317 user_group_member = self._find_user_in_group(user, user_group)
312 318
313 319 if not user_group_member:
314 320 # User isn't in that group
315 321 return False
316 322
317 323 try:
318 324 self.sa.delete(user_group_member)
319 325 except Exception:
320 326 log.error(traceback.format_exc())
321 327 raise
322 328
323 329 self._log_user_changes('removed from', user_group, user)
324 330 return True
325 331
326 332 def has_perm(self, user_group, perm):
327 333 user_group = self._get_user_group(user_group)
328 334 perm = self._get_perm(perm)
329 335
330 336 return UserGroupToPerm.query()\
331 337 .filter(UserGroupToPerm.users_group == user_group)\
332 338 .filter(UserGroupToPerm.permission == perm).scalar() is not None
333 339
334 340 def grant_perm(self, user_group, perm):
335 341 user_group = self._get_user_group(user_group)
336 342 perm = self._get_perm(perm)
337 343
338 344 # if this permission is already granted skip it
339 345 _perm = UserGroupToPerm.query()\
340 346 .filter(UserGroupToPerm.users_group == user_group)\
341 347 .filter(UserGroupToPerm.permission == perm)\
342 348 .scalar()
343 349 if _perm:
344 350 return
345 351
346 352 new = UserGroupToPerm()
347 353 new.users_group = user_group
348 354 new.permission = perm
349 355 self.sa.add(new)
350 356 return new
351 357
352 358 def revoke_perm(self, user_group, perm):
353 359 user_group = self._get_user_group(user_group)
354 360 perm = self._get_perm(perm)
355 361
356 362 obj = UserGroupToPerm.query()\
357 363 .filter(UserGroupToPerm.users_group == user_group)\
358 364 .filter(UserGroupToPerm.permission == perm).scalar()
359 365 if obj:
360 366 self.sa.delete(obj)
361 367
362 368 def grant_user_permission(self, user_group, user, perm):
363 369 """
364 370 Grant permission for user on given user group, or update
365 371 existing one if found
366 372
367 373 :param user_group: Instance of UserGroup, users_group_id,
368 374 or users_group_name
369 375 :param user: Instance of User, user_id or username
370 376 :param perm: Instance of Permission, or permission_name
371 377 """
372 378
373 379 user_group = self._get_user_group(user_group)
374 380 user = self._get_user(user)
375 381 permission = self._get_perm(perm)
376 382
377 383 # check if we have that permission already
378 384 obj = self.sa.query(UserUserGroupToPerm)\
379 385 .filter(UserUserGroupToPerm.user == user)\
380 386 .filter(UserUserGroupToPerm.user_group == user_group)\
381 387 .scalar()
382 388 if obj is None:
383 389 # create new !
384 390 obj = UserUserGroupToPerm()
385 391 obj.user_group = user_group
386 392 obj.user = user
387 393 obj.permission = permission
388 394 self.sa.add(obj)
389 395 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
390 396 action_logger_generic(
391 397 'granted permission: {} to user: {} on usergroup: {}'.format(
392 398 perm, user, user_group), namespace='security.usergroup')
393 399
394 400 return obj
395 401
396 402 def revoke_user_permission(self, user_group, user):
397 403 """
398 404 Revoke permission for user on given user group
399 405
400 406 :param user_group: Instance of UserGroup, users_group_id,
401 407 or users_group name
402 408 :param user: Instance of User, user_id or username
403 409 """
404 410
405 411 user_group = self._get_user_group(user_group)
406 412 user = self._get_user(user)
407 413
408 414 obj = self.sa.query(UserUserGroupToPerm)\
409 415 .filter(UserUserGroupToPerm.user == user)\
410 416 .filter(UserUserGroupToPerm.user_group == user_group)\
411 417 .scalar()
412 418 if obj:
413 419 self.sa.delete(obj)
414 420 log.debug('Revoked perm on %s on %s', user_group, user)
415 421 action_logger_generic(
416 422 'revoked permission from user: {} on usergroup: {}'.format(
417 423 user, user_group), namespace='security.usergroup')
418 424
419 425 def grant_user_group_permission(self, target_user_group, user_group, perm):
420 426 """
421 427 Grant user group permission for given target_user_group
422 428
423 429 :param target_user_group:
424 430 :param user_group:
425 431 :param perm:
426 432 """
427 433 target_user_group = self._get_user_group(target_user_group)
428 434 user_group = self._get_user_group(user_group)
429 435 permission = self._get_perm(perm)
430 436 # forbid assigning same user group to itself
431 437 if target_user_group == user_group:
432 438 raise RepoGroupAssignmentError('target repo:%s cannot be '
433 439 'assigned to itself' % target_user_group)
434 440
435 441 # check if we have that permission already
436 442 obj = self.sa.query(UserGroupUserGroupToPerm)\
437 443 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
438 444 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
439 445 .scalar()
440 446 if obj is None:
441 447 # create new !
442 448 obj = UserGroupUserGroupToPerm()
443 449 obj.user_group = user_group
444 450 obj.target_user_group = target_user_group
445 451 obj.permission = permission
446 452 self.sa.add(obj)
447 453 log.debug(
448 454 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
449 455 action_logger_generic(
450 456 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
451 457 perm, user_group, target_user_group),
452 458 namespace='security.usergroup')
453 459
454 460 return obj
455 461
456 462 def revoke_user_group_permission(self, target_user_group, user_group):
457 463 """
458 464 Revoke user group permission for given target_user_group
459 465
460 466 :param target_user_group:
461 467 :param user_group:
462 468 """
463 469 target_user_group = self._get_user_group(target_user_group)
464 470 user_group = self._get_user_group(user_group)
465 471
466 472 obj = self.sa.query(UserGroupUserGroupToPerm)\
467 473 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
468 474 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
469 475 .scalar()
470 476 if obj:
471 477 self.sa.delete(obj)
472 478 log.debug(
473 479 'Revoked perm on %s on %s', target_user_group, user_group)
474 480 action_logger_generic(
475 481 'revoked permission from usergroup: {} on usergroup: {}'.format(
476 482 user_group, target_user_group),
477 483 namespace='security.repogroup')
478 484
479 485 def enforce_groups(self, user, groups, extern_type=None):
480 486 user = self._get_user(user)
481 487 log.debug('Enforcing groups %s on user %s', groups, user)
482 488 current_groups = user.group_member
483 489 # find the external created groups
484 490 externals = [x.users_group for x in current_groups
485 491 if 'extern_type' in x.users_group.group_data]
486 492
487 493 # calculate from what groups user should be removed
488 494 # externals that are not in groups
489 495 for gr in externals:
490 496 if gr.users_group_name not in groups:
491 497 log.debug('Removing user %s from user group %s', user, gr)
492 498 self.remove_user_from_group(gr, user)
493 499
494 500 # now we calculate in which groups user should be == groups params
495 501 owner = User.get_first_admin().username
496 502 for gr in set(groups):
497 503 existing_group = UserGroup.get_by_group_name(gr)
498 504 if not existing_group:
499 505 desc = 'Automatically created from plugin:%s' % extern_type
500 506 # we use first admin account to set the owner of the group
501 507 existing_group = UserGroupModel().create(gr, desc, owner,
502 508 group_data={'extern_type': extern_type})
503 509
504 510 # we can only add users to special groups created via plugins
505 511 managed = 'extern_type' in existing_group.group_data
506 512 if managed:
507 513 log.debug('Adding user %s to user group %s', user, gr)
508 514 UserGroupModel().add_user_to_group(existing_group, user)
509 515 else:
510 516 log.debug('Skipping addition to group %s since it is '
511 517 'not managed by auth plugins' % gr)
General Comments 0
You need to be logged in to leave comments. Login now