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