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