##// END OF EJS Templates
api docstrings: drop the mostly useless :param autodoc
Mads Kiilerich -
r8751:c6c30609 stable
parent child Browse files
Show More
@@ -1,2392 +1,2135 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.controllers.api.api
15 kallithea.controllers.api.api
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 API controller for Kallithea
18 API controller for Kallithea
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Aug 20, 2011
22 :created_on: Aug 20, 2011
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 from datetime import datetime
30 from datetime import datetime
31
31
32 from tg import request
32 from tg import request
33
33
34 from kallithea.controllers.api import JSONRPCController, JSONRPCError
34 from kallithea.controllers.api import JSONRPCController, JSONRPCError
35 from kallithea.lib.auth import (AuthUser, HasPermissionAny, HasPermissionAnyDecorator, HasRepoGroupPermissionLevel, HasRepoPermissionLevel,
35 from kallithea.lib.auth import (AuthUser, HasPermissionAny, HasPermissionAnyDecorator, HasRepoGroupPermissionLevel, HasRepoPermissionLevel,
36 HasUserGroupPermissionLevel)
36 HasUserGroupPermissionLevel)
37 from kallithea.lib.exceptions import DefaultUserException, UserGroupsAssignedException
37 from kallithea.lib.exceptions import DefaultUserException, UserGroupsAssignedException
38 from kallithea.lib.utils import repo2db_mapper
38 from kallithea.lib.utils import repo2db_mapper
39 from kallithea.lib.vcs.backends.base import EmptyChangeset
39 from kallithea.lib.vcs.backends.base import EmptyChangeset
40 from kallithea.lib.vcs.exceptions import EmptyRepositoryError
40 from kallithea.lib.vcs.exceptions import EmptyRepositoryError
41 from kallithea.model import db, meta, userlog
41 from kallithea.model import db, meta, userlog
42 from kallithea.model.changeset_status import ChangesetStatusModel
42 from kallithea.model.changeset_status import ChangesetStatusModel
43 from kallithea.model.comment import ChangesetCommentsModel
43 from kallithea.model.comment import ChangesetCommentsModel
44 from kallithea.model.gist import GistModel
44 from kallithea.model.gist import GistModel
45 from kallithea.model.pull_request import PullRequestModel
45 from kallithea.model.pull_request import PullRequestModel
46 from kallithea.model.repo import RepoModel
46 from kallithea.model.repo import RepoModel
47 from kallithea.model.repo_group import RepoGroupModel
47 from kallithea.model.repo_group import RepoGroupModel
48 from kallithea.model.scm import ScmModel, UserGroupList
48 from kallithea.model.scm import ScmModel, UserGroupList
49 from kallithea.model.user import UserModel
49 from kallithea.model.user import UserModel
50 from kallithea.model.user_group import UserGroupModel
50 from kallithea.model.user_group import UserGroupModel
51
51
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 def store_update(updates, attr, name):
56 def store_update(updates, attr, name):
57 """
57 """
58 Stores param in updates dict if it's not None (i.e. if user explicitly set
58 Stores param in updates dict if it's not None (i.e. if user explicitly set
59 a parameter). This allows easy updates of passed in params.
59 a parameter). This allows easy updates of passed in params.
60 """
60 """
61 if attr is not None:
61 if attr is not None:
62 updates[name] = attr
62 updates[name] = attr
63
63
64
64
65 def get_user_or_error(userid):
65 def get_user_or_error(userid):
66 """
66 """
67 Get user by id or name or return JsonRPCError if not found
67 Get user by id or name or return JsonRPCError if not found
68
69 :param userid:
70 """
68 """
71 user = UserModel().get_user(userid)
69 user = UserModel().get_user(userid)
72 if user is None:
70 if user is None:
73 raise JSONRPCError("user `%s` does not exist" % (userid,))
71 raise JSONRPCError("user `%s` does not exist" % (userid,))
74 return user
72 return user
75
73
76
74
77 def get_repo_or_error(repoid):
75 def get_repo_or_error(repoid):
78 """
76 """
79 Get repo by id or name or return JsonRPCError if not found
77 Get repo by id or name or return JsonRPCError if not found
80
81 :param repoid:
82 """
78 """
83 repo = RepoModel().get_repo(repoid)
79 repo = RepoModel().get_repo(repoid)
84 if repo is None:
80 if repo is None:
85 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
81 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
86 return repo
82 return repo
87
83
88
84
89 def get_repo_group_or_error(repogroupid):
85 def get_repo_group_or_error(repogroupid):
90 """
86 """
91 Get repo group by id or name or return JsonRPCError if not found
87 Get repo group by id or name or return JsonRPCError if not found
92
93 :param repogroupid:
94 """
88 """
95 repo_group = db.RepoGroup.guess_instance(repogroupid)
89 repo_group = db.RepoGroup.guess_instance(repogroupid)
96 if repo_group is None:
90 if repo_group is None:
97 raise JSONRPCError(
91 raise JSONRPCError(
98 'repository group `%s` does not exist' % (repogroupid,))
92 'repository group `%s` does not exist' % (repogroupid,))
99 return repo_group
93 return repo_group
100
94
101
95
102 def get_user_group_or_error(usergroupid):
96 def get_user_group_or_error(usergroupid):
103 """
97 """
104 Get user group by id or name or return JsonRPCError if not found
98 Get user group by id or name or return JsonRPCError if not found
105
106 :param usergroupid:
107 """
99 """
108 user_group = UserGroupModel().get_group(usergroupid)
100 user_group = UserGroupModel().get_group(usergroupid)
109 if user_group is None:
101 if user_group is None:
110 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
102 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
111 return user_group
103 return user_group
112
104
113
105
114 def get_perm_or_error(permid, prefix=None):
106 def get_perm_or_error(permid, prefix=None):
115 """
107 """
116 Get permission by id or name or return JsonRPCError if not found
108 Get permission by id or name or return JsonRPCError if not found
117
118 :param permid:
119 """
109 """
120 perm = db.Permission.get_by_key(permid)
110 perm = db.Permission.get_by_key(permid)
121 if perm is None:
111 if perm is None:
122 raise JSONRPCError('permission `%s` does not exist' % (permid,))
112 raise JSONRPCError('permission `%s` does not exist' % (permid,))
123 if prefix:
113 if prefix:
124 if not perm.permission_name.startswith(prefix):
114 if not perm.permission_name.startswith(prefix):
125 raise JSONRPCError('permission `%s` is invalid, '
115 raise JSONRPCError('permission `%s` is invalid, '
126 'should start with %s' % (permid, prefix))
116 'should start with %s' % (permid, prefix))
127 return perm
117 return perm
128
118
129
119
130 def get_gist_or_error(gistid):
120 def get_gist_or_error(gistid):
131 """
121 """
132 Get gist by id or gist_access_id or return JsonRPCError if not found
122 Get gist by id or gist_access_id or return JsonRPCError if not found
133
134 :param gistid:
135 """
123 """
136 gist = GistModel().get_gist(gistid)
124 gist = GistModel().get_gist(gistid)
137 if gist is None:
125 if gist is None:
138 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
126 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
139 return gist
127 return gist
140
128
141
129
142 class ApiController(JSONRPCController):
130 class ApiController(JSONRPCController):
143 """
131 """
144 API Controller
132 API Controller
145
133
146 The authenticated user can be found as request.authuser.
134 The authenticated user can be found as request.authuser.
147
135
148 Example function::
136 Example function::
149
137
150 def func(arg1, arg2,...):
138 def func(arg1, arg2,...):
151 pass
139 pass
152
140
153 Each function should also **raise** JSONRPCError for any
141 Each function should also **raise** JSONRPCError for any
154 errors that happens.
142 errors that happens.
155 """
143 """
156
144
157 @HasPermissionAnyDecorator('hg.admin')
145 @HasPermissionAnyDecorator('hg.admin')
158 def test(self, args):
146 def test(self, args):
159 return args
147 return args
160
148
161 @HasPermissionAnyDecorator('hg.admin')
149 @HasPermissionAnyDecorator('hg.admin')
162 def pull(self, repoid, clone_uri=None):
150 def pull(self, repoid, clone_uri=None):
163 """
151 """
164 Triggers a pull from remote location on given repo. Can be used to
152 Triggers a pull from remote location on given repo. Can be used to
165 automatically keep remote repos up to date. This command can be executed
153 automatically keep remote repos up to date. This command can be executed
166 only using api_key belonging to user with admin rights
154 only using api_key belonging to user with admin rights
167
155
168 :param repoid: repository name or repository id
169 :type repoid: str or int
170 :param clone_uri: repository URI to pull from (optional)
171 :type clone_uri: str
172
173 OUTPUT::
156 OUTPUT::
174
157
175 id : <id_given_in_input>
158 id : <id_given_in_input>
176 result : {
159 result : {
177 "msg" : "Pulled from `<repository name>`",
160 "msg" : "Pulled from `<repository name>`",
178 "repository" : "<repository name>"
161 "repository" : "<repository name>"
179 }
162 }
180 error : null
163 error : null
181
164
182 ERROR OUTPUT::
165 ERROR OUTPUT::
183
166
184 id : <id_given_in_input>
167 id : <id_given_in_input>
185 result : null
168 result : null
186 error : {
169 error : {
187 "Unable to pull changes from `<reponame>`"
170 "Unable to pull changes from `<reponame>`"
188 }
171 }
189 """
172 """
190 repo = get_repo_or_error(repoid)
173 repo = get_repo_or_error(repoid)
191
174
192 try:
175 try:
193 ScmModel().pull_changes(repo.repo_name,
176 ScmModel().pull_changes(repo.repo_name,
194 request.authuser.username,
177 request.authuser.username,
195 request.ip_addr,
178 request.ip_addr,
196 clone_uri=clone_uri)
179 clone_uri=clone_uri)
197 return dict(
180 return dict(
198 msg='Pulled from `%s`' % repo.repo_name,
181 msg='Pulled from `%s`' % repo.repo_name,
199 repository=repo.repo_name
182 repository=repo.repo_name
200 )
183 )
201 except Exception:
184 except Exception:
202 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
203 raise JSONRPCError(
186 raise JSONRPCError(
204 'Unable to pull changes from `%s`' % repo.repo_name
187 'Unable to pull changes from `%s`' % repo.repo_name
205 )
188 )
206
189
207 @HasPermissionAnyDecorator('hg.admin')
190 @HasPermissionAnyDecorator('hg.admin')
208 def rescan_repos(self, remove_obsolete=False):
191 def rescan_repos(self, remove_obsolete=False):
209 """
192 """
210 Triggers rescan repositories action. If remove_obsolete is set
193 Triggers rescan repositories action. If remove_obsolete is set
211 than also delete repos that are in database but not in the filesystem.
194 than also delete repos that are in database but not in the filesystem.
212 aka "clean zombies". This command can be executed only using api_key
195 aka "clean zombies". This command can be executed only using api_key
213 belonging to user with admin rights.
196 belonging to user with admin rights.
214
197
215 :param remove_obsolete: deletes repositories from
216 database that are not found on the filesystem
217 :type remove_obsolete: Optional(bool)
218
219 OUTPUT::
198 OUTPUT::
220
199
221 id : <id_given_in_input>
200 id : <id_given_in_input>
222 result : {
201 result : {
223 'added': [<added repository name>,...]
202 'added': [<added repository name>,...]
224 'removed': [<removed repository name>,...]
203 'removed': [<removed repository name>,...]
225 }
204 }
226 error : null
205 error : null
227
206
228 ERROR OUTPUT::
207 ERROR OUTPUT::
229
208
230 id : <id_given_in_input>
209 id : <id_given_in_input>
231 result : null
210 result : null
232 error : {
211 error : {
233 'Error occurred during rescan repositories action'
212 'Error occurred during rescan repositories action'
234 }
213 }
235 """
214 """
236 try:
215 try:
237 rm_obsolete = remove_obsolete
216 rm_obsolete = remove_obsolete
238 added, removed = repo2db_mapper(ScmModel().repo_scan(),
217 added, removed = repo2db_mapper(ScmModel().repo_scan(),
239 remove_obsolete=rm_obsolete)
218 remove_obsolete=rm_obsolete)
240 return {'added': added, 'removed': removed}
219 return {'added': added, 'removed': removed}
241 except Exception:
220 except Exception:
242 log.error(traceback.format_exc())
221 log.error(traceback.format_exc())
243 raise JSONRPCError(
222 raise JSONRPCError(
244 'Error occurred during rescan repositories action'
223 'Error occurred during rescan repositories action'
245 )
224 )
246
225
247 def invalidate_cache(self, repoid):
226 def invalidate_cache(self, repoid):
248 """
227 """
249 Invalidate cache for repository.
228 Invalidate cache for repository.
250 This command can be executed only using api_key belonging to user with admin
229 This command can be executed only using api_key belonging to user with admin
251 rights or regular user that have write or admin or write access to repository.
230 rights or regular user that have write or admin or write access to repository.
252
231
253 :param repoid: repository name or repository id
254 :type repoid: str or int
255
256 OUTPUT::
232 OUTPUT::
257
233
258 id : <id_given_in_input>
234 id : <id_given_in_input>
259 result : {
235 result : {
260 'msg': Cache for repository `<repository name>` was invalidated,
236 'msg': Cache for repository `<repository name>` was invalidated,
261 'repository': <repository name>
237 'repository': <repository name>
262 }
238 }
263 error : null
239 error : null
264
240
265 ERROR OUTPUT::
241 ERROR OUTPUT::
266
242
267 id : <id_given_in_input>
243 id : <id_given_in_input>
268 result : null
244 result : null
269 error : {
245 error : {
270 'Error occurred during cache invalidation action'
246 'Error occurred during cache invalidation action'
271 }
247 }
272 """
248 """
273 repo = get_repo_or_error(repoid)
249 repo = get_repo_or_error(repoid)
274 if not HasPermissionAny('hg.admin')():
250 if not HasPermissionAny('hg.admin')():
275 if not HasRepoPermissionLevel('write')(repo.repo_name):
251 if not HasRepoPermissionLevel('write')(repo.repo_name):
276 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
252 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
277
253
278 try:
254 try:
279 ScmModel().mark_for_invalidation(repo.repo_name)
255 ScmModel().mark_for_invalidation(repo.repo_name)
280 return dict(
256 return dict(
281 msg='Cache for repository `%s` was invalidated' % (repoid,),
257 msg='Cache for repository `%s` was invalidated' % (repoid,),
282 repository=repo.repo_name
258 repository=repo.repo_name
283 )
259 )
284 except Exception:
260 except Exception:
285 log.error(traceback.format_exc())
261 log.error(traceback.format_exc())
286 raise JSONRPCError(
262 raise JSONRPCError(
287 'Error occurred during cache invalidation action'
263 'Error occurred during cache invalidation action'
288 )
264 )
289
265
290 @HasPermissionAnyDecorator('hg.admin')
266 @HasPermissionAnyDecorator('hg.admin')
291 def get_ip(self, userid=None):
267 def get_ip(self, userid=None):
292 """
268 """
293 Shows IP address as seen from Kallithea server, together with all
269 Shows IP address as seen from Kallithea server, together with all
294 defined IP addresses for given user. If userid is not passed data is
270 defined IP addresses for given user. If userid is not passed data is
295 returned for user who's calling this function.
271 returned for user who's calling this function.
296 This command can be executed only using api_key belonging to user with
272 This command can be executed only using api_key belonging to user with
297 admin rights.
273 admin rights.
298
274
299 :param userid: username to show ips for
300 :type userid: Optional(str or int)
301
302 OUTPUT::
275 OUTPUT::
303
276
304 id : <id_given_in_input>
277 id : <id_given_in_input>
305 result : {
278 result : {
306 "server_ip_addr" : "<ip_from_client>",
279 "server_ip_addr" : "<ip_from_client>",
307 "user_ips" : [
280 "user_ips" : [
308 {
281 {
309 "ip_addr" : "<ip_with_mask>",
282 "ip_addr" : "<ip_with_mask>",
310 "ip_range" : ["<start_ip>", "<end_ip>"]
283 "ip_range" : ["<start_ip>", "<end_ip>"]
311 },
284 },
312 ...
285 ...
313 ]
286 ]
314 }
287 }
315 error : null
288 error : null
316 """
289 """
317 if userid is None:
290 if userid is None:
318 userid = request.authuser.user_id
291 userid = request.authuser.user_id
319 user = get_user_or_error(userid)
292 user = get_user_or_error(userid)
320 ips = db.UserIpMap.query().filter(db.UserIpMap.user == user).all()
293 ips = db.UserIpMap.query().filter(db.UserIpMap.user == user).all()
321 return dict(
294 return dict(
322 server_ip_addr=request.ip_addr,
295 server_ip_addr=request.ip_addr,
323 user_ips=ips
296 user_ips=ips
324 )
297 )
325
298
326 # alias for old
299 # alias for old
327 show_ip = get_ip
300 show_ip = get_ip
328
301
329 @HasPermissionAnyDecorator('hg.admin')
302 @HasPermissionAnyDecorator('hg.admin')
330 def get_server_info(self):
303 def get_server_info(self):
331 """
304 """
332 return server info, including Kallithea version and installed packages
305 return server info, including Kallithea version and installed packages
333
306
334 OUTPUT::
307 OUTPUT::
335
308
336 id : <id_given_in_input>
309 id : <id_given_in_input>
337 result : {
310 result : {
338 'modules' : [ [<module name>, <module version>], ...]
311 'modules' : [ [<module name>, <module version>], ...]
339 'py_version' : <python version>,
312 'py_version' : <python version>,
340 'platform' : <platform type>,
313 'platform' : <platform type>,
341 'kallithea_version' : <kallithea version>,
314 'kallithea_version' : <kallithea version>,
342 'git_version' : '<git version>',
315 'git_version' : '<git version>',
343 'git_path' : '<git path>'
316 'git_path' : '<git path>'
344 }
317 }
345 error : null
318 error : null
346 """
319 """
347 return db.Setting.get_server_info()
320 return db.Setting.get_server_info()
348
321
349 def get_user(self, userid=None):
322 def get_user(self, userid=None):
350 """
323 """
351 Gets a user by username or user_id, Returns empty result if user is
324 Gets a user by username or user_id, Returns empty result if user is
352 not found. If userid param is skipped it is set to id of user who is
325 not found. If userid param is skipped it is set to id of user who is
353 calling this method. This command can be executed only using api_key
326 calling this method. This command can be executed only using api_key
354 belonging to user with admin rights, or regular users that cannot
327 belonging to user with admin rights, or regular users that cannot
355 specify different userid than theirs
328 specify different userid than theirs
356
329
357 :param userid: user to get data for
358 :type userid: Optional(str or int)
359
360 OUTPUT::
330 OUTPUT::
361
331
362 id : <id_given_in_input>
332 id : <id_given_in_input>
363 result : None if user does not exist or
333 result : None if user does not exist or
364 {
334 {
365 "user_id" : "<user_id>",
335 "user_id" : "<user_id>",
366 "username" : "<username>",
336 "username" : "<username>",
367 "firstname" : "<firstname>",
337 "firstname" : "<firstname>",
368 "lastname" : "<lastname>",
338 "lastname" : "<lastname>",
369 "email" : "<email>",
339 "email" : "<email>",
370 "emails" : "[<list of all emails including additional ones>]",
340 "emails" : "[<list of all emails including additional ones>]",
371 "active" : "<bool: user active>",
341 "active" : "<bool: user active>",
372 "admin" : "<bool: user is admin>",
342 "admin" : "<bool: user is admin>",
373 "permissions" : {
343 "permissions" : {
374 "global" : ["hg.create.repository",
344 "global" : ["hg.create.repository",
375 "repository.read",
345 "repository.read",
376 "hg.register.manual_activate"],
346 "hg.register.manual_activate"],
377 "repositories" : {"repo1" : "repository.none"},
347 "repositories" : {"repo1" : "repository.none"},
378 "repositories_groups" : {"Group1" : "group.read"},
348 "repositories_groups" : {"Group1" : "group.read"},
379 "user_groups" : { "usrgrp1" : "usergroup.admin" }
349 "user_groups" : { "usrgrp1" : "usergroup.admin" }
380 }
350 }
381 }
351 }
382 error : null
352 error : null
383 """
353 """
384 if not HasPermissionAny('hg.admin')():
354 if not HasPermissionAny('hg.admin')():
385 # make sure normal user does not pass someone else userid,
355 # make sure normal user does not pass someone else userid,
386 # he is not allowed to do that
356 # he is not allowed to do that
387 if userid is not None and userid != request.authuser.user_id:
357 if userid is not None and userid != request.authuser.user_id:
388 raise JSONRPCError(
358 raise JSONRPCError(
389 'userid is not the same as your user'
359 'userid is not the same as your user'
390 )
360 )
391
361
392 if userid is None:
362 if userid is None:
393 userid = request.authuser.user_id
363 userid = request.authuser.user_id
394
364
395 user = get_user_or_error(userid)
365 user = get_user_or_error(userid)
396 data = user.get_api_data()
366 data = user.get_api_data()
397 data['permissions'] = AuthUser(user_id=user.user_id).permissions
367 data['permissions'] = AuthUser(user_id=user.user_id).permissions
398 return data
368 return data
399
369
400 @HasPermissionAnyDecorator('hg.admin')
370 @HasPermissionAnyDecorator('hg.admin')
401 def get_users(self):
371 def get_users(self):
402 """
372 """
403 Lists all existing users. This command can be executed only using api_key
373 Lists all existing users. This command can be executed only using api_key
404 belonging to user with admin rights.
374 belonging to user with admin rights.
405
375
406 OUTPUT::
376 OUTPUT::
407
377
408 id : <id_given_in_input>
378 id : <id_given_in_input>
409 result : [<user_object>, ...]
379 result : [<user_object>, ...]
410 error : null
380 error : null
411 """
381 """
412 return [
382 return [
413 user.get_api_data()
383 user.get_api_data()
414 for user in db.User.query()
384 for user in db.User.query()
415 .order_by(db.User.username)
385 .order_by(db.User.username)
416 .filter_by(is_default_user=False)
386 .filter_by(is_default_user=False)
417 ]
387 ]
418
388
419 @HasPermissionAnyDecorator('hg.admin')
389 @HasPermissionAnyDecorator('hg.admin')
420 def create_user(self, username, email, password='',
390 def create_user(self, username, email, password='',
421 firstname='', lastname='',
391 firstname='', lastname='',
422 active=True, admin=False,
392 active=True, admin=False,
423 extern_type=db.User.DEFAULT_AUTH_TYPE,
393 extern_type=db.User.DEFAULT_AUTH_TYPE,
424 extern_name=''):
394 extern_name=''):
425 """
395 """
426 Creates new user. Returns new user object. This command can
396 Creates new user. Returns new user object. This command can
427 be executed only using api_key belonging to user with admin rights.
397 be executed only using api_key belonging to user with admin rights.
428
398
429 :param username: new username
430 :type username: str or int
431 :param email: email
432 :type email: str
433 :param password: password
434 :type password: Optional(str)
435 :param firstname: firstname
436 :type firstname: str
437 :param lastname: lastname
438 :type lastname: str
439 :param active: active
440 :type active: Optional(bool)
441 :param admin: admin
442 :type admin: Optional(bool)
443 :param extern_name: name of extern
444 :type extern_name: Optional(str)
445 :param extern_type: extern_type
446 :type extern_type: Optional(str)
447
448 OUTPUT::
399 OUTPUT::
449
400
450 id : <id_given_in_input>
401 id : <id_given_in_input>
451 result : {
402 result : {
452 "msg" : "created new user `<username>`",
403 "msg" : "created new user `<username>`",
453 "user" : <user_obj>
404 "user" : <user_obj>
454 }
405 }
455 error : null
406 error : null
456
407
457 ERROR OUTPUT::
408 ERROR OUTPUT::
458
409
459 id : <id_given_in_input>
410 id : <id_given_in_input>
460 result : null
411 result : null
461 error : {
412 error : {
462 "user `<username>` already exist"
413 "user `<username>` already exist"
463 or
414 or
464 "email `<email>` already exist"
415 "email `<email>` already exist"
465 or
416 or
466 "failed to create user `<username>`"
417 "failed to create user `<username>`"
467 }
418 }
468 """
419 """
469 if db.User.get_by_username(username):
420 if db.User.get_by_username(username):
470 raise JSONRPCError("user `%s` already exist" % (username,))
421 raise JSONRPCError("user `%s` already exist" % (username,))
471
422
472 if db.User.get_by_email(email):
423 if db.User.get_by_email(email):
473 raise JSONRPCError("email `%s` already exist" % (email,))
424 raise JSONRPCError("email `%s` already exist" % (email,))
474
425
475 try:
426 try:
476 user = UserModel().create_or_update(
427 user = UserModel().create_or_update(
477 username=username,
428 username=username,
478 password=password,
429 password=password,
479 email=email,
430 email=email,
480 firstname=firstname,
431 firstname=firstname,
481 lastname=lastname,
432 lastname=lastname,
482 active=active,
433 active=active,
483 admin=admin,
434 admin=admin,
484 extern_type=extern_type,
435 extern_type=extern_type,
485 extern_name=extern_name
436 extern_name=extern_name
486 )
437 )
487 meta.Session().commit()
438 meta.Session().commit()
488 return dict(
439 return dict(
489 msg='created new user `%s`' % username,
440 msg='created new user `%s`' % username,
490 user=user.get_api_data()
441 user=user.get_api_data()
491 )
442 )
492 except Exception:
443 except Exception:
493 log.error(traceback.format_exc())
444 log.error(traceback.format_exc())
494 raise JSONRPCError('failed to create user `%s`' % (username,))
445 raise JSONRPCError('failed to create user `%s`' % (username,))
495
446
496 @HasPermissionAnyDecorator('hg.admin')
447 @HasPermissionAnyDecorator('hg.admin')
497 def update_user(self, userid, username=None,
448 def update_user(self, userid, username=None,
498 email=None, password=None,
449 email=None, password=None,
499 firstname=None, lastname=None,
450 firstname=None, lastname=None,
500 active=None, admin=None,
451 active=None, admin=None,
501 extern_type=None, extern_name=None):
452 extern_type=None, extern_name=None):
502 """
453 """
503 updates given user if such user exists. This command can
454 updates given user if such user exists. This command can
504 be executed only using api_key belonging to user with admin rights.
455 be executed only using api_key belonging to user with admin rights.
505
456
506 :param userid: userid to update
507 :type userid: str or int
508 :param username: new username
509 :type username: Optional(str or int)
510 :param email: email
511 :type email: Optional(str)
512 :param password: password
513 :type password: Optional(str)
514 :param firstname: firstname
515 :type firstname: Optional(str)
516 :param lastname: lastname
517 :type lastname: Optional(str)
518 :param active: active
519 :type active: Optional(bool)
520 :param admin: admin
521 :type admin: Optional(bool)
522 :param extern_name:
523 :type extern_name: Optional(str)
524 :param extern_type:
525 :type extern_type: Optional(str)
526
527 OUTPUT::
457 OUTPUT::
528
458
529 id : <id_given_in_input>
459 id : <id_given_in_input>
530 result : {
460 result : {
531 "msg" : "updated user ID:<userid> <username>",
461 "msg" : "updated user ID:<userid> <username>",
532 "user" : <user_object>
462 "user" : <user_object>
533 }
463 }
534 error : null
464 error : null
535
465
536 ERROR OUTPUT::
466 ERROR OUTPUT::
537
467
538 id : <id_given_in_input>
468 id : <id_given_in_input>
539 result : null
469 result : null
540 error : {
470 error : {
541 "failed to update user `<username>`"
471 "failed to update user `<username>`"
542 }
472 }
543 """
473 """
544 user = get_user_or_error(userid)
474 user = get_user_or_error(userid)
545
475
546 # only non optional arguments will be stored in updates
476 # only non optional arguments will be stored in updates
547 updates = {}
477 updates = {}
548
478
549 try:
479 try:
550
480
551 store_update(updates, username, 'username')
481 store_update(updates, username, 'username')
552 store_update(updates, password, 'password')
482 store_update(updates, password, 'password')
553 store_update(updates, email, 'email')
483 store_update(updates, email, 'email')
554 store_update(updates, firstname, 'name')
484 store_update(updates, firstname, 'name')
555 store_update(updates, lastname, 'lastname')
485 store_update(updates, lastname, 'lastname')
556 store_update(updates, active, 'active')
486 store_update(updates, active, 'active')
557 store_update(updates, admin, 'admin')
487 store_update(updates, admin, 'admin')
558 store_update(updates, extern_name, 'extern_name')
488 store_update(updates, extern_name, 'extern_name')
559 store_update(updates, extern_type, 'extern_type')
489 store_update(updates, extern_type, 'extern_type')
560
490
561 user = UserModel().update_user(user, **updates)
491 user = UserModel().update_user(user, **updates)
562 meta.Session().commit()
492 meta.Session().commit()
563 return dict(
493 return dict(
564 msg='updated user ID:%s %s' % (user.user_id, user.username),
494 msg='updated user ID:%s %s' % (user.user_id, user.username),
565 user=user.get_api_data()
495 user=user.get_api_data()
566 )
496 )
567 except DefaultUserException:
497 except DefaultUserException:
568 log.error(traceback.format_exc())
498 log.error(traceback.format_exc())
569 raise JSONRPCError('editing default user is forbidden')
499 raise JSONRPCError('editing default user is forbidden')
570 except Exception:
500 except Exception:
571 log.error(traceback.format_exc())
501 log.error(traceback.format_exc())
572 raise JSONRPCError('failed to update user `%s`' % (userid,))
502 raise JSONRPCError('failed to update user `%s`' % (userid,))
573
503
574 @HasPermissionAnyDecorator('hg.admin')
504 @HasPermissionAnyDecorator('hg.admin')
575 def delete_user(self, userid):
505 def delete_user(self, userid):
576 """
506 """
577 deletes given user if such user exists. This command can
507 deletes given user if such user exists. This command can
578 be executed only using api_key belonging to user with admin rights.
508 be executed only using api_key belonging to user with admin rights.
579
509
580 :param userid: user to delete
581 :type userid: str or int
582
583 OUTPUT::
510 OUTPUT::
584
511
585 id : <id_given_in_input>
512 id : <id_given_in_input>
586 result : {
513 result : {
587 "msg" : "deleted user ID:<userid> <username>",
514 "msg" : "deleted user ID:<userid> <username>",
588 "user" : null
515 "user" : null
589 }
516 }
590 error : null
517 error : null
591
518
592 ERROR OUTPUT::
519 ERROR OUTPUT::
593
520
594 id : <id_given_in_input>
521 id : <id_given_in_input>
595 result : null
522 result : null
596 error : {
523 error : {
597 "failed to delete user ID:<userid> <username>"
524 "failed to delete user ID:<userid> <username>"
598 }
525 }
599 """
526 """
600 user = get_user_or_error(userid)
527 user = get_user_or_error(userid)
601
528
602 try:
529 try:
603 UserModel().delete(userid)
530 UserModel().delete(userid)
604 meta.Session().commit()
531 meta.Session().commit()
605 return dict(
532 return dict(
606 msg='deleted user ID:%s %s' % (user.user_id, user.username),
533 msg='deleted user ID:%s %s' % (user.user_id, user.username),
607 user=None
534 user=None
608 )
535 )
609 except Exception:
536 except Exception:
610
537
611 log.error(traceback.format_exc())
538 log.error(traceback.format_exc())
612 raise JSONRPCError('failed to delete user ID:%s %s'
539 raise JSONRPCError('failed to delete user ID:%s %s'
613 % (user.user_id, user.username))
540 % (user.user_id, user.username))
614
541
615 # permission check inside
542 # permission check inside
616 def get_user_group(self, usergroupid):
543 def get_user_group(self, usergroupid):
617 """
544 """
618 Gets an existing user group. This command can be executed only using api_key
545 Gets an existing user group. This command can be executed only using api_key
619 belonging to user with admin rights or user who has at least
546 belonging to user with admin rights or user who has at least
620 read access to user group.
547 read access to user group.
621
548
622 :param usergroupid: id of user_group to edit
623 :type usergroupid: str or int
624
625 OUTPUT::
549 OUTPUT::
626
550
627 id : <id_given_in_input>
551 id : <id_given_in_input>
628 result : None if group not exist
552 result : None if group not exist
629 {
553 {
630 "users_group_id" : "<id>",
554 "users_group_id" : "<id>",
631 "group_name" : "<groupname>",
555 "group_name" : "<groupname>",
632 "group_description" : "<description>",
556 "group_description" : "<description>",
633 "active" : "<bool>",
557 "active" : "<bool>",
634 "owner" : "<username>",
558 "owner" : "<username>",
635 "members" : [<user_obj>,...]
559 "members" : [<user_obj>,...]
636 }
560 }
637 error : null
561 error : null
638 """
562 """
639 user_group = get_user_group_or_error(usergroupid)
563 user_group = get_user_group_or_error(usergroupid)
640 if not HasPermissionAny('hg.admin')():
564 if not HasPermissionAny('hg.admin')():
641 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
565 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
642 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
566 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
643
567
644 data = user_group.get_api_data()
568 data = user_group.get_api_data()
645 return data
569 return data
646
570
647 # permission check inside
571 # permission check inside
648 def get_user_groups(self):
572 def get_user_groups(self):
649 """
573 """
650 Lists all existing user groups. This command can be executed only using
574 Lists all existing user groups. This command can be executed only using
651 api_key belonging to user with admin rights or user who has at least
575 api_key belonging to user with admin rights or user who has at least
652 read access to user group.
576 read access to user group.
653
577
654 OUTPUT::
578 OUTPUT::
655
579
656 id : <id_given_in_input>
580 id : <id_given_in_input>
657 result : [<user_group_obj>,...]
581 result : [<user_group_obj>,...]
658 error : null
582 error : null
659 """
583 """
660 return [
584 return [
661 user_group.get_api_data()
585 user_group.get_api_data()
662 for user_group in UserGroupList(db.UserGroup.query().all(), perm_level='read')
586 for user_group in UserGroupList(db.UserGroup.query().all(), perm_level='read')
663 ]
587 ]
664
588
665 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
589 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
666 def create_user_group(self, group_name, description='',
590 def create_user_group(self, group_name, description='',
667 owner=None, active=True):
591 owner=None, active=True):
668 """
592 """
669 Creates new user group. This command can be executed only using api_key
593 Creates new user group. This command can be executed only using api_key
670 belonging to user with admin rights or an user who has create user group
594 belonging to user with admin rights or an user who has create user group
671 permission
595 permission
672
596
673 :param group_name: name of new user group
674 :type group_name: str
675 :param description: group description
676 :type description: Optional(str)
677 :param owner: owner of group. If not passed apiuser is the owner
678 :type owner: Optional(str or int)
679 :param active: group is active
680 :type active: Optional(bool)
681
682 OUTPUT::
597 OUTPUT::
683
598
684 id : <id_given_in_input>
599 id : <id_given_in_input>
685 result : {
600 result : {
686 "msg" : "created new user group `<groupname>`",
601 "msg" : "created new user group `<groupname>`",
687 "user_group" : <user_group_object>
602 "user_group" : <user_group_object>
688 }
603 }
689 error : null
604 error : null
690
605
691 ERROR OUTPUT::
606 ERROR OUTPUT::
692
607
693 id : <id_given_in_input>
608 id : <id_given_in_input>
694 result : null
609 result : null
695 error : {
610 error : {
696 "user group `<group name>` already exist"
611 "user group `<group name>` already exist"
697 or
612 or
698 "failed to create group `<group name>`"
613 "failed to create group `<group name>`"
699 }
614 }
700 """
615 """
701 if UserGroupModel().get_by_name(group_name):
616 if UserGroupModel().get_by_name(group_name):
702 raise JSONRPCError("user group `%s` already exist" % (group_name,))
617 raise JSONRPCError("user group `%s` already exist" % (group_name,))
703
618
704 try:
619 try:
705 if owner is None:
620 if owner is None:
706 owner = request.authuser.user_id
621 owner = request.authuser.user_id
707
622
708 owner = get_user_or_error(owner)
623 owner = get_user_or_error(owner)
709 ug = UserGroupModel().create(name=group_name, description=description,
624 ug = UserGroupModel().create(name=group_name, description=description,
710 owner=owner, active=active)
625 owner=owner, active=active)
711 meta.Session().commit()
626 meta.Session().commit()
712 return dict(
627 return dict(
713 msg='created new user group `%s`' % group_name,
628 msg='created new user group `%s`' % group_name,
714 user_group=ug.get_api_data()
629 user_group=ug.get_api_data()
715 )
630 )
716 except Exception:
631 except Exception:
717 log.error(traceback.format_exc())
632 log.error(traceback.format_exc())
718 raise JSONRPCError('failed to create group `%s`' % (group_name,))
633 raise JSONRPCError('failed to create group `%s`' % (group_name,))
719
634
720 # permission check inside
635 # permission check inside
721 def update_user_group(self, usergroupid, group_name=None,
636 def update_user_group(self, usergroupid, group_name=None,
722 description=None, owner=None,
637 description=None, owner=None,
723 active=None):
638 active=None):
724 """
639 """
725 Updates given usergroup. This command can be executed only using api_key
640 Updates given usergroup. This command can be executed only using api_key
726 belonging to user with admin rights or an admin of given user group
641 belonging to user with admin rights or an admin of given user group
727
642
728 :param usergroupid: id of user group to update
729 :type usergroupid: str or int
730 :param group_name: name of new user group
731 :type group_name: str
732 :param description: group description
733 :type description: str
734 :param owner: owner of group.
735 :type owner: Optional(str or int)
736 :param active: group is active
737 :type active: Optional(bool)
738
739 OUTPUT::
643 OUTPUT::
740
644
741 id : <id_given_in_input>
645 id : <id_given_in_input>
742 result : {
646 result : {
743 "msg" : 'updated user group ID:<user group id> <user group name>',
647 "msg" : 'updated user group ID:<user group id> <user group name>',
744 "user_group" : <user_group_object>
648 "user_group" : <user_group_object>
745 }
649 }
746 error : null
650 error : null
747
651
748 ERROR OUTPUT::
652 ERROR OUTPUT::
749
653
750 id : <id_given_in_input>
654 id : <id_given_in_input>
751 result : null
655 result : null
752 error : {
656 error : {
753 "failed to update user group `<user group name>`"
657 "failed to update user group `<user group name>`"
754 }
658 }
755 """
659 """
756 user_group = get_user_group_or_error(usergroupid)
660 user_group = get_user_group_or_error(usergroupid)
757 if not HasPermissionAny('hg.admin')():
661 if not HasPermissionAny('hg.admin')():
758 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
662 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
759 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
663 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
760
664
761 if owner is not None:
665 if owner is not None:
762 owner = get_user_or_error(owner)
666 owner = get_user_or_error(owner)
763
667
764 updates = {}
668 updates = {}
765 store_update(updates, group_name, 'users_group_name')
669 store_update(updates, group_name, 'users_group_name')
766 store_update(updates, description, 'user_group_description')
670 store_update(updates, description, 'user_group_description')
767 store_update(updates, owner, 'owner')
671 store_update(updates, owner, 'owner')
768 store_update(updates, active, 'users_group_active')
672 store_update(updates, active, 'users_group_active')
769 try:
673 try:
770 UserGroupModel().update(user_group, updates)
674 UserGroupModel().update(user_group, updates)
771 meta.Session().commit()
675 meta.Session().commit()
772 return dict(
676 return dict(
773 msg='updated user group ID:%s %s' % (user_group.users_group_id,
677 msg='updated user group ID:%s %s' % (user_group.users_group_id,
774 user_group.users_group_name),
678 user_group.users_group_name),
775 user_group=user_group.get_api_data()
679 user_group=user_group.get_api_data()
776 )
680 )
777 except Exception:
681 except Exception:
778 log.error(traceback.format_exc())
682 log.error(traceback.format_exc())
779 raise JSONRPCError('failed to update user group `%s`' % (usergroupid,))
683 raise JSONRPCError('failed to update user group `%s`' % (usergroupid,))
780
684
781 # permission check inside
685 # permission check inside
782 def delete_user_group(self, usergroupid):
686 def delete_user_group(self, usergroupid):
783 """
687 """
784 Delete given user group by user group id or name.
688 Delete given user group by user group id or name.
785 This command can be executed only using api_key
689 This command can be executed only using api_key
786 belonging to user with admin rights or an admin of given user group
690 belonging to user with admin rights or an admin of given user group
787
691
788 :param usergroupid:
789 :type usergroupid: str or int
790
791 OUTPUT::
692 OUTPUT::
792
693
793 id : <id_given_in_input>
694 id : <id_given_in_input>
794 result : {
695 result : {
795 "msg" : "deleted user group ID:<user_group_id> <user_group_name>"
696 "msg" : "deleted user group ID:<user_group_id> <user_group_name>"
796 }
697 }
797 error : null
698 error : null
798
699
799 ERROR OUTPUT::
700 ERROR OUTPUT::
800
701
801 id : <id_given_in_input>
702 id : <id_given_in_input>
802 result : null
703 result : null
803 error : {
704 error : {
804 "failed to delete user group ID:<user_group_id> <user_group_name>"
705 "failed to delete user group ID:<user_group_id> <user_group_name>"
805 or
706 or
806 "RepoGroup assigned to <repo_groups_list>"
707 "RepoGroup assigned to <repo_groups_list>"
807 }
708 }
808 """
709 """
809 user_group = get_user_group_or_error(usergroupid)
710 user_group = get_user_group_or_error(usergroupid)
810 if not HasPermissionAny('hg.admin')():
711 if not HasPermissionAny('hg.admin')():
811 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
712 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
812 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
713 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
813
714
814 try:
715 try:
815 UserGroupModel().delete(user_group)
716 UserGroupModel().delete(user_group)
816 meta.Session().commit()
717 meta.Session().commit()
817 return dict(
718 return dict(
818 msg='deleted user group ID:%s %s' %
719 msg='deleted user group ID:%s %s' %
819 (user_group.users_group_id, user_group.users_group_name),
720 (user_group.users_group_id, user_group.users_group_name),
820 user_group=None
721 user_group=None
821 )
722 )
822 except UserGroupsAssignedException as e:
723 except UserGroupsAssignedException as e:
823 log.error(traceback.format_exc())
724 log.error(traceback.format_exc())
824 raise JSONRPCError(str(e))
725 raise JSONRPCError(str(e))
825 except Exception:
726 except Exception:
826 log.error(traceback.format_exc())
727 log.error(traceback.format_exc())
827 raise JSONRPCError('failed to delete user group ID:%s %s' %
728 raise JSONRPCError('failed to delete user group ID:%s %s' %
828 (user_group.users_group_id,
729 (user_group.users_group_id,
829 user_group.users_group_name)
730 user_group.users_group_name)
830 )
731 )
831
732
832 # permission check inside
733 # permission check inside
833 def add_user_to_user_group(self, usergroupid, userid):
734 def add_user_to_user_group(self, usergroupid, userid):
834 """
735 """
835 Adds a user to a user group. If user exists in that group success will be
736 Adds a user to a user group. If user exists in that group success will be
836 `false`. This command can be executed only using api_key
737 `false`. This command can be executed only using api_key
837 belonging to user with admin rights or an admin of a given user group
738 belonging to user with admin rights or an admin of a given user group
838
739
839 :param usergroupid:
840 :type usergroupid: str or int
841 :param userid:
842 :type userid: str or int
843
844 OUTPUT::
740 OUTPUT::
845
741
846 id : <id_given_in_input>
742 id : <id_given_in_input>
847 result : {
743 result : {
848 "success" : True|False # depends on if member is in group
744 "success" : True|False # depends on if member is in group
849 "msg" : "added member `<username>` to a user group `<groupname>` |
745 "msg" : "added member `<username>` to a user group `<groupname>` |
850 User is already in that group"
746 User is already in that group"
851 }
747 }
852 error : null
748 error : null
853
749
854 ERROR OUTPUT::
750 ERROR OUTPUT::
855
751
856 id : <id_given_in_input>
752 id : <id_given_in_input>
857 result : null
753 result : null
858 error : {
754 error : {
859 "failed to add member to user group `<user_group_name>`"
755 "failed to add member to user group `<user_group_name>`"
860 }
756 }
861 """
757 """
862 user = get_user_or_error(userid)
758 user = get_user_or_error(userid)
863 user_group = get_user_group_or_error(usergroupid)
759 user_group = get_user_group_or_error(usergroupid)
864 if not HasPermissionAny('hg.admin')():
760 if not HasPermissionAny('hg.admin')():
865 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
761 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
866 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
762 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
867
763
868 try:
764 try:
869 ugm = UserGroupModel().add_user_to_group(user_group, user)
765 ugm = UserGroupModel().add_user_to_group(user_group, user)
870 success = True if ugm is not True else False
766 success = True if ugm is not True else False
871 msg = 'added member `%s` to user group `%s`' % (
767 msg = 'added member `%s` to user group `%s`' % (
872 user.username, user_group.users_group_name
768 user.username, user_group.users_group_name
873 )
769 )
874 msg = msg if success else 'User is already in that group'
770 msg = msg if success else 'User is already in that group'
875 meta.Session().commit()
771 meta.Session().commit()
876
772
877 return dict(
773 return dict(
878 success=success,
774 success=success,
879 msg=msg
775 msg=msg
880 )
776 )
881 except Exception:
777 except Exception:
882 log.error(traceback.format_exc())
778 log.error(traceback.format_exc())
883 raise JSONRPCError(
779 raise JSONRPCError(
884 'failed to add member to user group `%s`' % (
780 'failed to add member to user group `%s`' % (
885 user_group.users_group_name,
781 user_group.users_group_name,
886 )
782 )
887 )
783 )
888
784
889 # permission check inside
785 # permission check inside
890 def remove_user_from_user_group(self, usergroupid, userid):
786 def remove_user_from_user_group(self, usergroupid, userid):
891 """
787 """
892 Removes a user from a user group. If user is not in given group success will
788 Removes a user from a user group. If user is not in given group success will
893 be `false`. This command can be executed only
789 be `false`. This command can be executed only
894 using api_key belonging to user with admin rights or an admin of given user group
790 using api_key belonging to user with admin rights or an admin of given user group
895
791
896 :param usergroupid:
897 :param userid:
898
899 OUTPUT::
792 OUTPUT::
900
793
901 id : <id_given_in_input>
794 id : <id_given_in_input>
902 result : {
795 result : {
903 "success" : True|False, # depends on if member is in group
796 "success" : True|False, # depends on if member is in group
904 "msg" : "removed member <username> from user group <groupname> |
797 "msg" : "removed member <username> from user group <groupname> |
905 User wasn't in group"
798 User wasn't in group"
906 }
799 }
907 error : null
800 error : null
908 """
801 """
909 user = get_user_or_error(userid)
802 user = get_user_or_error(userid)
910 user_group = get_user_group_or_error(usergroupid)
803 user_group = get_user_group_or_error(usergroupid)
911 if not HasPermissionAny('hg.admin')():
804 if not HasPermissionAny('hg.admin')():
912 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
805 if not HasUserGroupPermissionLevel('admin')(user_group.users_group_name):
913 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
806 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
914
807
915 try:
808 try:
916 success = UserGroupModel().remove_user_from_group(user_group, user)
809 success = UserGroupModel().remove_user_from_group(user_group, user)
917 msg = 'removed member `%s` from user group `%s`' % (
810 msg = 'removed member `%s` from user group `%s`' % (
918 user.username, user_group.users_group_name
811 user.username, user_group.users_group_name
919 )
812 )
920 msg = msg if success else "User wasn't in group"
813 msg = msg if success else "User wasn't in group"
921 meta.Session().commit()
814 meta.Session().commit()
922 return dict(success=success, msg=msg)
815 return dict(success=success, msg=msg)
923 except Exception:
816 except Exception:
924 log.error(traceback.format_exc())
817 log.error(traceback.format_exc())
925 raise JSONRPCError(
818 raise JSONRPCError(
926 'failed to remove member from user group `%s`' % (
819 'failed to remove member from user group `%s`' % (
927 user_group.users_group_name,
820 user_group.users_group_name,
928 )
821 )
929 )
822 )
930
823
931 # permission check inside
824 # permission check inside
932 def get_repo(self, repoid,
825 def get_repo(self, repoid,
933 with_revision_names=False,
826 with_revision_names=False,
934 with_pullrequests=False):
827 with_pullrequests=False):
935 """
828 """
936 Gets an existing repository by it's name or repository_id. Members will return
829 Gets an existing repository by it's name or repository_id. Members will return
937 either users_group or user associated to that repository. This command can be
830 either users_group or user associated to that repository. This command can be
938 executed only using api_key belonging to user with admin
831 executed only using api_key belonging to user with admin
939 rights or regular user that have at least read access to repository.
832 rights or regular user that have at least read access to repository.
940
833
941 :param repoid: repository name or repository id
942 :type repoid: str or int
943
944 OUTPUT::
834 OUTPUT::
945
835
946 id : <id_given_in_input>
836 id : <id_given_in_input>
947 result : {
837 result : {
948 "repo_id" : "<repo_id>",
838 "repo_id" : "<repo_id>",
949 "repo_name" : "<reponame>",
839 "repo_name" : "<reponame>",
950 "repo_type" : "<repo_type>",
840 "repo_type" : "<repo_type>",
951 "clone_uri" : "<clone_uri>",
841 "clone_uri" : "<clone_uri>",
952 "enable_downloads" : "<bool>",
842 "enable_downloads" : "<bool>",
953 "enable_statistics": "<bool>",
843 "enable_statistics": "<bool>",
954 "private" : "<bool>",
844 "private" : "<bool>",
955 "created_on" : "<date_time_created>",
845 "created_on" : "<date_time_created>",
956 "description" : "<description>",
846 "description" : "<description>",
957 "landing_rev" : "<landing_rev>",
847 "landing_rev" : "<landing_rev>",
958 "last_changeset" : {
848 "last_changeset" : {
959 "author" : "<full_author>",
849 "author" : "<full_author>",
960 "date" : "<date_time_of_commit>",
850 "date" : "<date_time_of_commit>",
961 "message" : "<commit_message>",
851 "message" : "<commit_message>",
962 "raw_id" : "<raw_id>",
852 "raw_id" : "<raw_id>",
963 "revision": "<numeric_revision>",
853 "revision": "<numeric_revision>",
964 "short_id": "<short_id>"
854 "short_id": "<short_id>"
965 },
855 },
966 "owner" : "<repo_owner>",
856 "owner" : "<repo_owner>",
967 "fork_of" : "<name_of_fork_parent>",
857 "fork_of" : "<name_of_fork_parent>",
968 "members" : [
858 "members" : [
969 {
859 {
970 "name" : "<username>",
860 "name" : "<username>",
971 "type" : "user",
861 "type" : "user",
972 "permission" : "repository.(read|write|admin)"
862 "permission" : "repository.(read|write|admin)"
973 },
863 },
974 …
864 …
975 {
865 {
976 "name" : "<usergroup name>",
866 "name" : "<usergroup name>",
977 "type" : "user_group",
867 "type" : "user_group",
978 "permission" : "usergroup.(read|write|admin)"
868 "permission" : "usergroup.(read|write|admin)"
979 },
869 },
980 …
870 …
981 ],
871 ],
982 "followers" : [<user_obj>, ...],
872 "followers" : [<user_obj>, ...],
983 <if with_revision_names == True>
873 <if with_revision_names == True>
984 "tags" : {
874 "tags" : {
985 "<tagname>" : "<raw_id>",
875 "<tagname>" : "<raw_id>",
986 ...
876 ...
987 },
877 },
988 "branches" : {
878 "branches" : {
989 "<branchname>" : "<raw_id>",
879 "<branchname>" : "<raw_id>",
990 ...
880 ...
991 },
881 },
992 "bookmarks" : {
882 "bookmarks" : {
993 "<bookmarkname>" : "<raw_id>",
883 "<bookmarkname>" : "<raw_id>",
994 ...
884 ...
995 }
885 }
996 }
886 }
997 error : null
887 error : null
998 """
888 """
999 repo = get_repo_or_error(repoid)
889 repo = get_repo_or_error(repoid)
1000
890
1001 if not HasPermissionAny('hg.admin')():
891 if not HasPermissionAny('hg.admin')():
1002 if not HasRepoPermissionLevel('read')(repo.repo_name):
892 if not HasRepoPermissionLevel('read')(repo.repo_name):
1003 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
893 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1004
894
1005 members = []
895 members = []
1006 for user in repo.repo_to_perm:
896 for user in repo.repo_to_perm:
1007 perm = user.permission.permission_name
897 perm = user.permission.permission_name
1008 user = user.user
898 user = user.user
1009 user_data = {
899 user_data = {
1010 'name': user.username,
900 'name': user.username,
1011 'type': "user",
901 'type': "user",
1012 'permission': perm
902 'permission': perm
1013 }
903 }
1014 members.append(user_data)
904 members.append(user_data)
1015
905
1016 for user_group in repo.users_group_to_perm:
906 for user_group in repo.users_group_to_perm:
1017 perm = user_group.permission.permission_name
907 perm = user_group.permission.permission_name
1018 user_group = user_group.users_group
908 user_group = user_group.users_group
1019 user_group_data = {
909 user_group_data = {
1020 'name': user_group.users_group_name,
910 'name': user_group.users_group_name,
1021 'type': "user_group",
911 'type': "user_group",
1022 'permission': perm
912 'permission': perm
1023 }
913 }
1024 members.append(user_group_data)
914 members.append(user_group_data)
1025
915
1026 followers = [
916 followers = [
1027 uf.user.get_api_data()
917 uf.user.get_api_data()
1028 for uf in repo.followers
918 for uf in repo.followers
1029 ]
919 ]
1030
920
1031 data = repo.get_api_data(with_revision_names=with_revision_names,
921 data = repo.get_api_data(with_revision_names=with_revision_names,
1032 with_pullrequests=with_pullrequests)
922 with_pullrequests=with_pullrequests)
1033 data['members'] = members
923 data['members'] = members
1034 data['followers'] = followers
924 data['followers'] = followers
1035 return data
925 return data
1036
926
1037 # permission check inside
927 # permission check inside
1038 def get_repos(self):
928 def get_repos(self):
1039 """
929 """
1040 Lists all existing repositories. This command can be executed only using
930 Lists all existing repositories. This command can be executed only using
1041 api_key belonging to user with admin rights or regular user that have
931 api_key belonging to user with admin rights or regular user that have
1042 admin, write or read access to repository.
932 admin, write or read access to repository.
1043
933
1044 OUTPUT::
934 OUTPUT::
1045
935
1046 id : <id_given_in_input>
936 id : <id_given_in_input>
1047 result : [
937 result : [
1048 {
938 {
1049 "repo_id" : "<repo_id>",
939 "repo_id" : "<repo_id>",
1050 "repo_name" : "<reponame>",
940 "repo_name" : "<reponame>",
1051 "repo_type" : "<repo_type>",
941 "repo_type" : "<repo_type>",
1052 "clone_uri" : "<clone_uri>",
942 "clone_uri" : "<clone_uri>",
1053 "private" : "<bool>",
943 "private" : "<bool>",
1054 "created_on" : "<datetimecreated>",
944 "created_on" : "<datetimecreated>",
1055 "description" : "<description>",
945 "description" : "<description>",
1056 "landing_rev" : "<landing_rev>",
946 "landing_rev" : "<landing_rev>",
1057 "owner" : "<repo_owner>",
947 "owner" : "<repo_owner>",
1058 "fork_of" : "<name_of_fork_parent>",
948 "fork_of" : "<name_of_fork_parent>",
1059 "enable_downloads" : "<bool>",
949 "enable_downloads" : "<bool>",
1060 "enable_statistics": "<bool>"
950 "enable_statistics": "<bool>"
1061 },
951 },
1062 …
952 …
1063 ]
953 ]
1064 error : null
954 error : null
1065 """
955 """
1066 if not HasPermissionAny('hg.admin')():
956 if not HasPermissionAny('hg.admin')():
1067 repos = request.authuser.get_all_user_repos()
957 repos = request.authuser.get_all_user_repos()
1068 else:
958 else:
1069 repos = db.Repository.query()
959 repos = db.Repository.query()
1070
960
1071 return [
961 return [
1072 repo.get_api_data()
962 repo.get_api_data()
1073 for repo in repos
963 for repo in repos
1074 ]
964 ]
1075
965
1076 # permission check inside
966 # permission check inside
1077 def get_repo_nodes(self, repoid, revision, root_path,
967 def get_repo_nodes(self, repoid, revision, root_path,
1078 ret_type='all'):
968 ret_type='all'):
1079 """
969 """
1080 returns a list of nodes and it's children in a flat list for a given path
970 returns a list of nodes and it's children in a flat list for a given path
1081 at given revision. It's possible to specify ret_type to show only `files` or
971 at given revision. It's possible to specify ret_type to show only `files` or
1082 `dirs`. This command can be executed only using api_key belonging to
972 `dirs`. This command can be executed only using api_key belonging to
1083 user with admin rights or regular user that have at least read access to repository.
973 user with admin rights or regular user that have at least read access to repository.
1084
974
1085 :param repoid: repository name or repository id
1086 :type repoid: str or int
1087 :param revision: revision for which listing should be done
1088 :type revision: str
1089 :param root_path: path from which start displaying
1090 :type root_path: str
1091 :param ret_type: return type 'all|files|dirs' nodes
1092 :type ret_type: Optional(str)
1093
1094 OUTPUT::
975 OUTPUT::
1095
976
1096 id : <id_given_in_input>
977 id : <id_given_in_input>
1097 result : [
978 result : [
1098 {
979 {
1099 "name" : "<name>",
980 "name" : "<name>",
1100 "type" : "<type>"
981 "type" : "<type>"
1101 },
982 },
1102 …
983 …
1103 ]
984 ]
1104 error : null
985 error : null
1105 """
986 """
1106 repo = get_repo_or_error(repoid)
987 repo = get_repo_or_error(repoid)
1107
988
1108 if not HasPermissionAny('hg.admin')():
989 if not HasPermissionAny('hg.admin')():
1109 if not HasRepoPermissionLevel('read')(repo.repo_name):
990 if not HasRepoPermissionLevel('read')(repo.repo_name):
1110 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
991 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1111
992
1112 _map = {}
993 _map = {}
1113 try:
994 try:
1114 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
995 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
1115 flat=False)
996 flat=False)
1116 _map = {
997 _map = {
1117 'all': _d + _f,
998 'all': _d + _f,
1118 'files': _f,
999 'files': _f,
1119 'dirs': _d,
1000 'dirs': _d,
1120 }
1001 }
1121 return _map[ret_type]
1002 return _map[ret_type]
1122 except KeyError:
1003 except KeyError:
1123 raise JSONRPCError('ret_type must be one of %s'
1004 raise JSONRPCError('ret_type must be one of %s'
1124 % (','.join(sorted(_map))))
1005 % (','.join(sorted(_map))))
1125 except Exception:
1006 except Exception:
1126 log.error(traceback.format_exc())
1007 log.error(traceback.format_exc())
1127 raise JSONRPCError(
1008 raise JSONRPCError(
1128 'failed to get repo: `%s` nodes' % repo.repo_name
1009 'failed to get repo: `%s` nodes' % repo.repo_name
1129 )
1010 )
1130
1011
1131 # permission check inside
1012 # permission check inside
1132 def create_repo(self, repo_name, owner=None,
1013 def create_repo(self, repo_name, owner=None,
1133 repo_type=None, description='',
1014 repo_type=None, description='',
1134 private=False, clone_uri=None,
1015 private=False, clone_uri=None,
1135 landing_rev='rev:tip',
1016 landing_rev='rev:tip',
1136 enable_statistics=None,
1017 enable_statistics=None,
1137 enable_downloads=None,
1018 enable_downloads=None,
1138 copy_permissions=False):
1019 copy_permissions=False):
1139 """
1020 """
1140 Creates a repository. The repository name contains the full path, but the
1021 Creates a repository. The repository name contains the full path, but the
1141 parent repository group must exist. For example "foo/bar/baz" require the groups
1022 parent repository group must exist. For example "foo/bar/baz" require the groups
1142 "foo" and "bar" (with "foo" as parent), and create "baz" repository with
1023 "foo" and "bar" (with "foo" as parent), and create "baz" repository with
1143 "bar" as group. This command can be executed only using api_key
1024 "bar" as group. This command can be executed only using api_key
1144 belonging to user with admin rights or regular user that have create
1025 belonging to user with admin rights or regular user that have create
1145 repository permission. Regular users cannot specify owner parameter
1026 repository permission. Regular users cannot specify owner parameter
1146
1027
1147 :param repo_name: repository name
1148 :type repo_name: str
1149 :param owner: user_id or username
1150 :type owner: Optional(str)
1151 :param repo_type: 'hg' or 'git'
1152 :type repo_type: Optional(str)
1153 :param description: repository description
1154 :type description: Optional(str)
1155 :param private:
1156 :type private: bool
1157 :param clone_uri:
1158 :type clone_uri: str
1159 :param landing_rev: <rev_type>:<rev>
1160 :type landing_rev: str
1161 :param enable_downloads:
1162 :type enable_downloads: bool
1163 :param enable_statistics:
1164 :type enable_statistics: bool
1165 :param copy_permissions: Copy permission from group that repository is
1166 being created.
1167 :type copy_permissions: bool
1168
1169 OUTPUT::
1028 OUTPUT::
1170
1029
1171 id : <id_given_in_input>
1030 id : <id_given_in_input>
1172 result : {
1031 result : {
1173 "msg" : "Created new repository `<reponame>`",
1032 "msg" : "Created new repository `<reponame>`",
1174 "success" : true
1033 "success" : true
1175 }
1034 }
1176 error : null
1035 error : null
1177
1036
1178 ERROR OUTPUT::
1037 ERROR OUTPUT::
1179
1038
1180 id : <id_given_in_input>
1039 id : <id_given_in_input>
1181 result : null
1040 result : null
1182 error : {
1041 error : {
1183 'failed to create repository `<repo_name>`
1042 'failed to create repository `<repo_name>`
1184 }
1043 }
1185 """
1044 """
1186 group_name = None
1045 group_name = None
1187 repo_name_parts = repo_name.split('/')
1046 repo_name_parts = repo_name.split('/')
1188 if len(repo_name_parts) > 1:
1047 if len(repo_name_parts) > 1:
1189 group_name = '/'.join(repo_name_parts[:-1])
1048 group_name = '/'.join(repo_name_parts[:-1])
1190 repo_group = db.RepoGroup.get_by_group_name(group_name)
1049 repo_group = db.RepoGroup.get_by_group_name(group_name)
1191 if repo_group is None:
1050 if repo_group is None:
1192 raise JSONRPCError("repo group `%s` not found" % group_name)
1051 raise JSONRPCError("repo group `%s` not found" % group_name)
1193 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1052 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1194 raise JSONRPCError("no permission to create repo in %s" % group_name)
1053 raise JSONRPCError("no permission to create repo in %s" % group_name)
1195 else:
1054 else:
1196 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1055 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1197 raise JSONRPCError("no permission to create top level repo")
1056 raise JSONRPCError("no permission to create top level repo")
1198
1057
1199 if not HasPermissionAny('hg.admin')():
1058 if not HasPermissionAny('hg.admin')():
1200 if owner is not None:
1059 if owner is not None:
1201 # forbid setting owner for non-admins
1060 # forbid setting owner for non-admins
1202 raise JSONRPCError(
1061 raise JSONRPCError(
1203 'Only Kallithea admin can specify `owner` param'
1062 'Only Kallithea admin can specify `owner` param'
1204 )
1063 )
1205 if owner is None:
1064 if owner is None:
1206 owner = request.authuser.user_id
1065 owner = request.authuser.user_id
1207
1066
1208 owner = get_user_or_error(owner)
1067 owner = get_user_or_error(owner)
1209
1068
1210 if RepoModel().get_by_repo_name(repo_name):
1069 if RepoModel().get_by_repo_name(repo_name):
1211 raise JSONRPCError("repo `%s` already exist" % repo_name)
1070 raise JSONRPCError("repo `%s` already exist" % repo_name)
1212
1071
1213 defs = db.Setting.get_default_repo_settings(strip_prefix=True)
1072 defs = db.Setting.get_default_repo_settings(strip_prefix=True)
1214 if private is None:
1073 if private is None:
1215 private = defs.get('repo_private') or False
1074 private = defs.get('repo_private') or False
1216 if repo_type is None:
1075 if repo_type is None:
1217 repo_type = defs.get('repo_type')
1076 repo_type = defs.get('repo_type')
1218 if enable_statistics is None:
1077 if enable_statistics is None:
1219 enable_statistics = defs.get('repo_enable_statistics')
1078 enable_statistics = defs.get('repo_enable_statistics')
1220 if enable_downloads is None:
1079 if enable_downloads is None:
1221 enable_downloads = defs.get('repo_enable_downloads')
1080 enable_downloads = defs.get('repo_enable_downloads')
1222
1081
1223 try:
1082 try:
1224 data = dict(
1083 data = dict(
1225 repo_name=repo_name_parts[-1],
1084 repo_name=repo_name_parts[-1],
1226 repo_name_full=repo_name,
1085 repo_name_full=repo_name,
1227 repo_type=repo_type,
1086 repo_type=repo_type,
1228 repo_description=description,
1087 repo_description=description,
1229 repo_private=private,
1088 repo_private=private,
1230 clone_uri=clone_uri,
1089 clone_uri=clone_uri,
1231 repo_group=group_name,
1090 repo_group=group_name,
1232 repo_landing_rev=landing_rev,
1091 repo_landing_rev=landing_rev,
1233 repo_enable_statistics=enable_statistics,
1092 repo_enable_statistics=enable_statistics,
1234 repo_enable_downloads=enable_downloads,
1093 repo_enable_downloads=enable_downloads,
1235 repo_copy_permissions=copy_permissions,
1094 repo_copy_permissions=copy_permissions,
1236 )
1095 )
1237
1096
1238 RepoModel().create(form_data=data, cur_user=owner.username)
1097 RepoModel().create(form_data=data, cur_user=owner.username)
1239 # no commit, it's done in RepoModel, or async via celery
1098 # no commit, it's done in RepoModel, or async via celery
1240 return dict(
1099 return dict(
1241 msg="Created new repository `%s`" % (repo_name,),
1100 msg="Created new repository `%s`" % (repo_name,),
1242 success=True, # cannot return the repo data here since fork
1101 success=True, # cannot return the repo data here since fork
1243 # can be done async
1102 # can be done async
1244 )
1103 )
1245 except Exception:
1104 except Exception:
1246 log.error(traceback.format_exc())
1105 log.error(traceback.format_exc())
1247 raise JSONRPCError(
1106 raise JSONRPCError(
1248 'failed to create repository `%s`' % (repo_name,))
1107 'failed to create repository `%s`' % (repo_name,))
1249
1108
1250 # permission check inside
1109 # permission check inside
1251 def update_repo(self, repoid, name=None,
1110 def update_repo(self, repoid, name=None,
1252 owner=None,
1111 owner=None,
1253 group=None,
1112 group=None,
1254 description=None, private=None,
1113 description=None, private=None,
1255 clone_uri=None, landing_rev=None,
1114 clone_uri=None, landing_rev=None,
1256 enable_statistics=None,
1115 enable_statistics=None,
1257 enable_downloads=None):
1116 enable_downloads=None):
1258 """
1117 """
1259 Updates repo
1118 Updates repo
1260
1261 :param repoid: repository name or repository id
1262 :type repoid: str or int
1263 :param name:
1264 :param owner:
1265 :param group:
1266 :param description:
1267 :param private:
1268 :param clone_uri:
1269 :param landing_rev:
1270 :param enable_statistics:
1271 :param enable_downloads:
1272 """
1119 """
1273 repo = get_repo_or_error(repoid)
1120 repo = get_repo_or_error(repoid)
1274 if not HasPermissionAny('hg.admin')():
1121 if not HasPermissionAny('hg.admin')():
1275 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1122 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1276 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1123 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1277
1124
1278 if (name != repo.repo_name and repo.group_id is None and
1125 if (name != repo.repo_name and repo.group_id is None and
1279 not HasPermissionAny('hg.create.repository')()
1126 not HasPermissionAny('hg.create.repository')()
1280 ):
1127 ):
1281 raise JSONRPCError('no permission to create (or move) top level repositories')
1128 raise JSONRPCError('no permission to create (or move) top level repositories')
1282
1129
1283 if owner is not None:
1130 if owner is not None:
1284 # forbid setting owner for non-admins
1131 # forbid setting owner for non-admins
1285 raise JSONRPCError(
1132 raise JSONRPCError(
1286 'Only Kallithea admin can specify `owner` param'
1133 'Only Kallithea admin can specify `owner` param'
1287 )
1134 )
1288
1135
1289 updates = {}
1136 updates = {}
1290 repo_group = group
1137 repo_group = group
1291 if repo_group is not None:
1138 if repo_group is not None:
1292 repo_group = get_repo_group_or_error(repo_group) # TODO: repos can thus currently not be moved to root
1139 repo_group = get_repo_group_or_error(repo_group) # TODO: repos can thus currently not be moved to root
1293 if repo_group.group_id != repo.group_id:
1140 if repo_group.group_id != repo.group_id:
1294 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(repo_group.group_name)):
1141 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(repo_group.group_name)):
1295 raise JSONRPCError("no permission to create (or move) repo in %s" % repo_group.group_name)
1142 raise JSONRPCError("no permission to create (or move) repo in %s" % repo_group.group_name)
1296 repo_group = repo_group.group_id
1143 repo_group = repo_group.group_id
1297 try:
1144 try:
1298 store_update(updates, name, 'repo_name')
1145 store_update(updates, name, 'repo_name')
1299 store_update(updates, repo_group, 'repo_group')
1146 store_update(updates, repo_group, 'repo_group')
1300 store_update(updates, owner, 'owner')
1147 store_update(updates, owner, 'owner')
1301 store_update(updates, description, 'repo_description')
1148 store_update(updates, description, 'repo_description')
1302 store_update(updates, private, 'repo_private')
1149 store_update(updates, private, 'repo_private')
1303 store_update(updates, clone_uri, 'clone_uri')
1150 store_update(updates, clone_uri, 'clone_uri')
1304 store_update(updates, landing_rev, 'repo_landing_rev')
1151 store_update(updates, landing_rev, 'repo_landing_rev')
1305 store_update(updates, enable_statistics, 'repo_enable_statistics')
1152 store_update(updates, enable_statistics, 'repo_enable_statistics')
1306 store_update(updates, enable_downloads, 'repo_enable_downloads')
1153 store_update(updates, enable_downloads, 'repo_enable_downloads')
1307
1154
1308 RepoModel().update(repo, **updates)
1155 RepoModel().update(repo, **updates)
1309 meta.Session().commit()
1156 meta.Session().commit()
1310 return dict(
1157 return dict(
1311 msg='updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
1158 msg='updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
1312 repository=repo.get_api_data()
1159 repository=repo.get_api_data()
1313 )
1160 )
1314 except Exception:
1161 except Exception:
1315 log.error(traceback.format_exc())
1162 log.error(traceback.format_exc())
1316 raise JSONRPCError('failed to update repo `%s`' % repoid)
1163 raise JSONRPCError('failed to update repo `%s`' % repoid)
1317
1164
1318 # permission check inside
1165 # permission check inside
1319 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
1166 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
1320 def fork_repo(self, repoid, fork_name,
1167 def fork_repo(self, repoid, fork_name,
1321 owner=None,
1168 owner=None,
1322 description='', copy_permissions=False,
1169 description='', copy_permissions=False,
1323 private=False, landing_rev='rev:tip'):
1170 private=False, landing_rev='rev:tip'):
1324 """
1171 """
1325 Creates a fork of given repo. In case of using celery this will
1172 Creates a fork of given repo. In case of using celery this will
1326 immediately return success message, while fork is going to be created
1173 immediately return success message, while fork is going to be created
1327 asynchronous. This command can be executed only using api_key belonging to
1174 asynchronous. This command can be executed only using api_key belonging to
1328 user with admin rights or regular user that have fork permission, and at least
1175 user with admin rights or regular user that have fork permission, and at least
1329 read access to forking repository. Regular users cannot specify owner parameter.
1176 read access to forking repository. Regular users cannot specify owner parameter.
1330
1177
1331 :param repoid: repository name or repository id
1332 :type repoid: str or int
1333 :param fork_name:
1334 :param owner:
1335 :param description:
1336 :param copy_permissions:
1337 :param private:
1338 :param landing_rev:
1339
1340 INPUT::
1178 INPUT::
1341
1179
1342 id : <id_for_response>
1180 id : <id_for_response>
1343 api_key : "<api_key>"
1181 api_key : "<api_key>"
1344 method : "fork_repo"
1182 method : "fork_repo"
1345 args : {
1183 args : {
1346 "repoid" : "<reponame or repo_id>",
1184 "repoid" : "<reponame or repo_id>",
1347 "fork_name" : "<forkname>",
1185 "fork_name" : "<forkname>",
1348 "owner" : "<username or user_id = Optional(=apiuser)>",
1186 "owner" : "<username or user_id = Optional(=apiuser)>",
1349 "description" : "<description>",
1187 "description" : "<description>",
1350 "copy_permissions": "<bool>",
1188 "copy_permissions": "<bool>",
1351 "private" : "<bool>",
1189 "private" : "<bool>",
1352 "landing_rev" : "<landing_rev>"
1190 "landing_rev" : "<landing_rev>"
1353 }
1191 }
1354
1192
1355 OUTPUT::
1193 OUTPUT::
1356
1194
1357 id : <id_given_in_input>
1195 id : <id_given_in_input>
1358 result : {
1196 result : {
1359 "msg" : "Created fork of `<reponame>` as `<forkname>`",
1197 "msg" : "Created fork of `<reponame>` as `<forkname>`",
1360 "success" : true
1198 "success" : true
1361 }
1199 }
1362 error : null
1200 error : null
1363 """
1201 """
1364 repo = get_repo_or_error(repoid)
1202 repo = get_repo_or_error(repoid)
1365 repo_name = repo.repo_name
1203 repo_name = repo.repo_name
1366
1204
1367 _repo = RepoModel().get_by_repo_name(fork_name)
1205 _repo = RepoModel().get_by_repo_name(fork_name)
1368 if _repo:
1206 if _repo:
1369 type_ = 'fork' if _repo.fork else 'repo'
1207 type_ = 'fork' if _repo.fork else 'repo'
1370 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1208 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1371
1209
1372 group_name = None
1210 group_name = None
1373 fork_name_parts = fork_name.split('/')
1211 fork_name_parts = fork_name.split('/')
1374 if len(fork_name_parts) > 1:
1212 if len(fork_name_parts) > 1:
1375 group_name = '/'.join(fork_name_parts[:-1])
1213 group_name = '/'.join(fork_name_parts[:-1])
1376 repo_group = db.RepoGroup.get_by_group_name(group_name)
1214 repo_group = db.RepoGroup.get_by_group_name(group_name)
1377 if repo_group is None:
1215 if repo_group is None:
1378 raise JSONRPCError("repo group `%s` not found" % group_name)
1216 raise JSONRPCError("repo group `%s` not found" % group_name)
1379 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1217 if not(HasPermissionAny('hg.admin')() or HasRepoGroupPermissionLevel('write')(group_name)):
1380 raise JSONRPCError("no permission to create repo in %s" % group_name)
1218 raise JSONRPCError("no permission to create repo in %s" % group_name)
1381 else:
1219 else:
1382 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1220 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
1383 raise JSONRPCError("no permission to create top level repo")
1221 raise JSONRPCError("no permission to create top level repo")
1384
1222
1385 if HasPermissionAny('hg.admin')():
1223 if HasPermissionAny('hg.admin')():
1386 pass
1224 pass
1387 elif HasRepoPermissionLevel('read')(repo.repo_name):
1225 elif HasRepoPermissionLevel('read')(repo.repo_name):
1388 if owner is not None:
1226 if owner is not None:
1389 # forbid setting owner for non-admins
1227 # forbid setting owner for non-admins
1390 raise JSONRPCError(
1228 raise JSONRPCError(
1391 'Only Kallithea admin can specify `owner` param'
1229 'Only Kallithea admin can specify `owner` param'
1392 )
1230 )
1393 else:
1231 else:
1394 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1232 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1395
1233
1396 if owner is None:
1234 if owner is None:
1397 owner = request.authuser.user_id
1235 owner = request.authuser.user_id
1398
1236
1399 owner = get_user_or_error(owner)
1237 owner = get_user_or_error(owner)
1400
1238
1401 try:
1239 try:
1402 form_data = dict(
1240 form_data = dict(
1403 repo_name=fork_name_parts[-1],
1241 repo_name=fork_name_parts[-1],
1404 repo_name_full=fork_name,
1242 repo_name_full=fork_name,
1405 repo_group=group_name,
1243 repo_group=group_name,
1406 repo_type=repo.repo_type,
1244 repo_type=repo.repo_type,
1407 description=description,
1245 description=description,
1408 private=private,
1246 private=private,
1409 copy_permissions=copy_permissions,
1247 copy_permissions=copy_permissions,
1410 landing_rev=landing_rev,
1248 landing_rev=landing_rev,
1411 update_after_clone=False,
1249 update_after_clone=False,
1412 fork_parent_id=repo.repo_id,
1250 fork_parent_id=repo.repo_id,
1413 )
1251 )
1414 RepoModel().create_fork(form_data, cur_user=owner.username)
1252 RepoModel().create_fork(form_data, cur_user=owner.username)
1415 # no commit, it's done in RepoModel, or async via celery
1253 # no commit, it's done in RepoModel, or async via celery
1416 return dict(
1254 return dict(
1417 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
1255 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
1418 fork_name),
1256 fork_name),
1419 success=True, # cannot return the repo data here since fork
1257 success=True, # cannot return the repo data here since fork
1420 # can be done async
1258 # can be done async
1421 )
1259 )
1422 except Exception:
1260 except Exception:
1423 log.error(traceback.format_exc())
1261 log.error(traceback.format_exc())
1424 raise JSONRPCError(
1262 raise JSONRPCError(
1425 'failed to fork repository `%s` as `%s`' % (repo_name,
1263 'failed to fork repository `%s` as `%s`' % (repo_name,
1426 fork_name)
1264 fork_name)
1427 )
1265 )
1428
1266
1429 # permission check inside
1267 # permission check inside
1430 def delete_repo(self, repoid, forks=''):
1268 def delete_repo(self, repoid, forks=''):
1431 """
1269 """
1432 Deletes a repository. This command can be executed only using api_key belonging
1270 Deletes a repository. This command can be executed only using api_key belonging
1433 to user with admin rights or regular user that have admin access to repository.
1271 to user with admin rights or regular user that have admin access to repository.
1434 When `forks` param is set it's possible to detach or delete forks of deleting
1272 When `forks` param is set it's possible to detach or delete forks of deleting
1435 repository
1273 repository
1436
1274
1437 :param repoid: repository name or repository id
1438 :type repoid: str or int
1439 :param forks: `detach` or `delete`, what do do with attached forks for repo
1440 :type forks: Optional(str)
1441
1442 OUTPUT::
1275 OUTPUT::
1443
1276
1444 id : <id_given_in_input>
1277 id : <id_given_in_input>
1445 result : {
1278 result : {
1446 "msg" : "Deleted repository `<reponame>`",
1279 "msg" : "Deleted repository `<reponame>`",
1447 "success" : true
1280 "success" : true
1448 }
1281 }
1449 error : null
1282 error : null
1450 """
1283 """
1451 repo = get_repo_or_error(repoid)
1284 repo = get_repo_or_error(repoid)
1452
1285
1453 if not HasPermissionAny('hg.admin')():
1286 if not HasPermissionAny('hg.admin')():
1454 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1287 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1455 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1288 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1456
1289
1457 try:
1290 try:
1458 handle_forks = forks
1291 handle_forks = forks
1459 _forks_msg = ''
1292 _forks_msg = ''
1460 _forks = [f for f in repo.forks]
1293 _forks = [f for f in repo.forks]
1461 if handle_forks == 'detach':
1294 if handle_forks == 'detach':
1462 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1295 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1463 elif handle_forks == 'delete':
1296 elif handle_forks == 'delete':
1464 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1297 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1465 elif _forks:
1298 elif _forks:
1466 raise JSONRPCError(
1299 raise JSONRPCError(
1467 'Cannot delete `%s` it still contains attached forks' %
1300 'Cannot delete `%s` it still contains attached forks' %
1468 (repo.repo_name,)
1301 (repo.repo_name,)
1469 )
1302 )
1470
1303
1471 RepoModel().delete(repo, forks=forks)
1304 RepoModel().delete(repo, forks=forks)
1472 meta.Session().commit()
1305 meta.Session().commit()
1473 return dict(
1306 return dict(
1474 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
1307 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
1475 success=True
1308 success=True
1476 )
1309 )
1477 except Exception:
1310 except Exception:
1478 log.error(traceback.format_exc())
1311 log.error(traceback.format_exc())
1479 raise JSONRPCError(
1312 raise JSONRPCError(
1480 'failed to delete repository `%s`' % (repo.repo_name,)
1313 'failed to delete repository `%s`' % (repo.repo_name,)
1481 )
1314 )
1482
1315
1483 @HasPermissionAnyDecorator('hg.admin')
1316 @HasPermissionAnyDecorator('hg.admin')
1484 def grant_user_permission(self, repoid, userid, perm):
1317 def grant_user_permission(self, repoid, userid, perm):
1485 """
1318 """
1486 Grant permission for user on given repository, or update existing one
1319 Grant permission for user on given repository, or update existing one
1487 if found. This command can be executed only using api_key belonging to user
1320 if found. This command can be executed only using api_key belonging to user
1488 with admin rights.
1321 with admin rights.
1489
1322
1490 :param repoid: repository name or repository id
1491 :type repoid: str or int
1492 :param userid:
1493 :param perm: (repository.(none|read|write|admin))
1494 :type perm: str
1495
1496 OUTPUT::
1323 OUTPUT::
1497
1324
1498 id : <id_given_in_input>
1325 id : <id_given_in_input>
1499 result : {
1326 result : {
1500 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1327 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1501 "success" : true
1328 "success" : true
1502 }
1329 }
1503 error : null
1330 error : null
1504 """
1331 """
1505 repo = get_repo_or_error(repoid)
1332 repo = get_repo_or_error(repoid)
1506 user = get_user_or_error(userid)
1333 user = get_user_or_error(userid)
1507 perm = get_perm_or_error(perm)
1334 perm = get_perm_or_error(perm)
1508
1335
1509 try:
1336 try:
1510
1337
1511 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1338 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1512
1339
1513 meta.Session().commit()
1340 meta.Session().commit()
1514 return dict(
1341 return dict(
1515 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1342 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1516 perm.permission_name, user.username, repo.repo_name
1343 perm.permission_name, user.username, repo.repo_name
1517 ),
1344 ),
1518 success=True
1345 success=True
1519 )
1346 )
1520 except Exception:
1347 except Exception:
1521 log.error(traceback.format_exc())
1348 log.error(traceback.format_exc())
1522 raise JSONRPCError(
1349 raise JSONRPCError(
1523 'failed to edit permission for user: `%s` in repo: `%s`' % (
1350 'failed to edit permission for user: `%s` in repo: `%s`' % (
1524 userid, repoid
1351 userid, repoid
1525 )
1352 )
1526 )
1353 )
1527
1354
1528 @HasPermissionAnyDecorator('hg.admin')
1355 @HasPermissionAnyDecorator('hg.admin')
1529 def revoke_user_permission(self, repoid, userid):
1356 def revoke_user_permission(self, repoid, userid):
1530 """
1357 """
1531 Revoke permission for user on given repository. This command can be executed
1358 Revoke permission for user on given repository. This command can be executed
1532 only using api_key belonging to user with admin rights.
1359 only using api_key belonging to user with admin rights.
1533
1360
1534 :param repoid: repository name or repository id
1535 :type repoid: str or int
1536 :param userid:
1537
1538 OUTPUT::
1361 OUTPUT::
1539
1362
1540 id : <id_given_in_input>
1363 id : <id_given_in_input>
1541 result : {
1364 result : {
1542 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1365 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1543 "success" : true
1366 "success" : true
1544 }
1367 }
1545 error : null
1368 error : null
1546 """
1369 """
1547 repo = get_repo_or_error(repoid)
1370 repo = get_repo_or_error(repoid)
1548 user = get_user_or_error(userid)
1371 user = get_user_or_error(userid)
1549 try:
1372 try:
1550 RepoModel().revoke_user_permission(repo=repo, user=user)
1373 RepoModel().revoke_user_permission(repo=repo, user=user)
1551 meta.Session().commit()
1374 meta.Session().commit()
1552 return dict(
1375 return dict(
1553 msg='Revoked perm for user: `%s` in repo: `%s`' % (
1376 msg='Revoked perm for user: `%s` in repo: `%s`' % (
1554 user.username, repo.repo_name
1377 user.username, repo.repo_name
1555 ),
1378 ),
1556 success=True
1379 success=True
1557 )
1380 )
1558 except Exception:
1381 except Exception:
1559 log.error(traceback.format_exc())
1382 log.error(traceback.format_exc())
1560 raise JSONRPCError(
1383 raise JSONRPCError(
1561 'failed to edit permission for user: `%s` in repo: `%s`' % (
1384 'failed to edit permission for user: `%s` in repo: `%s`' % (
1562 userid, repoid
1385 userid, repoid
1563 )
1386 )
1564 )
1387 )
1565
1388
1566 # permission check inside
1389 # permission check inside
1567 def grant_user_group_permission(self, repoid, usergroupid, perm):
1390 def grant_user_group_permission(self, repoid, usergroupid, perm):
1568 """
1391 """
1569 Grant permission for user group on given repository, or update
1392 Grant permission for user group on given repository, or update
1570 existing one if found. This command can be executed only using
1393 existing one if found. This command can be executed only using
1571 api_key belonging to user with admin rights.
1394 api_key belonging to user with admin rights.
1572
1395
1573 :param repoid: repository name or repository id
1574 :type repoid: str or int
1575 :param usergroupid: id of usergroup
1576 :type usergroupid: str or int
1577 :param perm: (repository.(none|read|write|admin))
1578 :type perm: str
1579
1580 OUTPUT::
1396 OUTPUT::
1581
1397
1582 id : <id_given_in_input>
1398 id : <id_given_in_input>
1583 result : {
1399 result : {
1584 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1400 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1585 "success" : true
1401 "success" : true
1586 }
1402 }
1587 error : null
1403 error : null
1588
1404
1589 ERROR OUTPUT::
1405 ERROR OUTPUT::
1590
1406
1591 id : <id_given_in_input>
1407 id : <id_given_in_input>
1592 result : null
1408 result : null
1593 error : {
1409 error : {
1594 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1410 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1595 }
1411 }
1596 """
1412 """
1597 repo = get_repo_or_error(repoid)
1413 repo = get_repo_or_error(repoid)
1598 perm = get_perm_or_error(perm)
1414 perm = get_perm_or_error(perm)
1599 user_group = get_user_group_or_error(usergroupid)
1415 user_group = get_user_group_or_error(usergroupid)
1600 if not HasPermissionAny('hg.admin')():
1416 if not HasPermissionAny('hg.admin')():
1601 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1417 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1602 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1418 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1603
1419
1604 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1420 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1605 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1421 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1606
1422
1607 try:
1423 try:
1608 RepoModel().grant_user_group_permission(
1424 RepoModel().grant_user_group_permission(
1609 repo=repo, group_name=user_group, perm=perm)
1425 repo=repo, group_name=user_group, perm=perm)
1610
1426
1611 meta.Session().commit()
1427 meta.Session().commit()
1612 return dict(
1428 return dict(
1613 msg='Granted perm: `%s` for user group: `%s` in '
1429 msg='Granted perm: `%s` for user group: `%s` in '
1614 'repo: `%s`' % (
1430 'repo: `%s`' % (
1615 perm.permission_name, user_group.users_group_name,
1431 perm.permission_name, user_group.users_group_name,
1616 repo.repo_name
1432 repo.repo_name
1617 ),
1433 ),
1618 success=True
1434 success=True
1619 )
1435 )
1620 except Exception:
1436 except Exception:
1621 log.error(traceback.format_exc())
1437 log.error(traceback.format_exc())
1622 raise JSONRPCError(
1438 raise JSONRPCError(
1623 'failed to edit permission for user group: `%s` in '
1439 'failed to edit permission for user group: `%s` in '
1624 'repo: `%s`' % (
1440 'repo: `%s`' % (
1625 usergroupid, repo.repo_name
1441 usergroupid, repo.repo_name
1626 )
1442 )
1627 )
1443 )
1628
1444
1629 # permission check inside
1445 # permission check inside
1630 def revoke_user_group_permission(self, repoid, usergroupid):
1446 def revoke_user_group_permission(self, repoid, usergroupid):
1631 """
1447 """
1632 Revoke permission for user group on given repository. This command can be
1448 Revoke permission for user group on given repository. This command can be
1633 executed only using api_key belonging to user with admin rights.
1449 executed only using api_key belonging to user with admin rights.
1634
1450
1635 :param repoid: repository name or repository id
1636 :type repoid: str or int
1637 :param usergroupid:
1638
1639 OUTPUT::
1451 OUTPUT::
1640
1452
1641 id : <id_given_in_input>
1453 id : <id_given_in_input>
1642 result : {
1454 result : {
1643 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1455 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1644 "success" : true
1456 "success" : true
1645 }
1457 }
1646 error : null
1458 error : null
1647 """
1459 """
1648 repo = get_repo_or_error(repoid)
1460 repo = get_repo_or_error(repoid)
1649 user_group = get_user_group_or_error(usergroupid)
1461 user_group = get_user_group_or_error(usergroupid)
1650 if not HasPermissionAny('hg.admin')():
1462 if not HasPermissionAny('hg.admin')():
1651 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1463 if not HasRepoPermissionLevel('admin')(repo.repo_name):
1652 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1464 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
1653
1465
1654 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1466 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1655 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1467 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
1656
1468
1657 try:
1469 try:
1658 RepoModel().revoke_user_group_permission(
1470 RepoModel().revoke_user_group_permission(
1659 repo=repo, group_name=user_group)
1471 repo=repo, group_name=user_group)
1660
1472
1661 meta.Session().commit()
1473 meta.Session().commit()
1662 return dict(
1474 return dict(
1663 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1475 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1664 user_group.users_group_name, repo.repo_name
1476 user_group.users_group_name, repo.repo_name
1665 ),
1477 ),
1666 success=True
1478 success=True
1667 )
1479 )
1668 except Exception:
1480 except Exception:
1669 log.error(traceback.format_exc())
1481 log.error(traceback.format_exc())
1670 raise JSONRPCError(
1482 raise JSONRPCError(
1671 'failed to edit permission for user group: `%s` in '
1483 'failed to edit permission for user group: `%s` in '
1672 'repo: `%s`' % (
1484 'repo: `%s`' % (
1673 user_group.users_group_name, repo.repo_name
1485 user_group.users_group_name, repo.repo_name
1674 )
1486 )
1675 )
1487 )
1676
1488
1677 @HasPermissionAnyDecorator('hg.admin')
1489 @HasPermissionAnyDecorator('hg.admin')
1678 def get_repo_group(self, repogroupid):
1490 def get_repo_group(self, repogroupid):
1679 """
1491 """
1680 Returns given repo group together with permissions, and repositories
1492 Returns given repo group together with permissions, and repositories
1681 inside the group
1493 inside the group
1682
1683 :param repogroupid: id/name of repository group
1684 :type repogroupid: str or int
1685 """
1494 """
1686 repo_group = get_repo_group_or_error(repogroupid)
1495 repo_group = get_repo_group_or_error(repogroupid)
1687
1496
1688 members = []
1497 members = []
1689 for user in repo_group.repo_group_to_perm:
1498 for user in repo_group.repo_group_to_perm:
1690 perm = user.permission.permission_name
1499 perm = user.permission.permission_name
1691 user = user.user
1500 user = user.user
1692 user_data = {
1501 user_data = {
1693 'name': user.username,
1502 'name': user.username,
1694 'type': "user",
1503 'type': "user",
1695 'permission': perm
1504 'permission': perm
1696 }
1505 }
1697 members.append(user_data)
1506 members.append(user_data)
1698
1507
1699 for user_group in repo_group.users_group_to_perm:
1508 for user_group in repo_group.users_group_to_perm:
1700 perm = user_group.permission.permission_name
1509 perm = user_group.permission.permission_name
1701 user_group = user_group.users_group
1510 user_group = user_group.users_group
1702 user_group_data = {
1511 user_group_data = {
1703 'name': user_group.users_group_name,
1512 'name': user_group.users_group_name,
1704 'type': "user_group",
1513 'type': "user_group",
1705 'permission': perm
1514 'permission': perm
1706 }
1515 }
1707 members.append(user_group_data)
1516 members.append(user_group_data)
1708
1517
1709 data = repo_group.get_api_data()
1518 data = repo_group.get_api_data()
1710 data["members"] = members
1519 data["members"] = members
1711 return data
1520 return data
1712
1521
1713 @HasPermissionAnyDecorator('hg.admin')
1522 @HasPermissionAnyDecorator('hg.admin')
1714 def get_repo_groups(self):
1523 def get_repo_groups(self):
1715 """
1524 """
1716 Returns all repository groups
1525 Returns all repository groups
1717 """
1526 """
1718 return [
1527 return [
1719 repo_group.get_api_data()
1528 repo_group.get_api_data()
1720 for repo_group in db.RepoGroup.query()
1529 for repo_group in db.RepoGroup.query()
1721 ]
1530 ]
1722
1531
1723 @HasPermissionAnyDecorator('hg.admin')
1532 @HasPermissionAnyDecorator('hg.admin')
1724 def create_repo_group(self, group_name, description='',
1533 def create_repo_group(self, group_name, description='',
1725 owner=None,
1534 owner=None,
1726 parent=None,
1535 parent=None,
1727 copy_permissions=False):
1536 copy_permissions=False):
1728 """
1537 """
1729 Creates a repository group. This command can be executed only using
1538 Creates a repository group. This command can be executed only using
1730 api_key belonging to user with admin rights.
1539 api_key belonging to user with admin rights.
1731
1540
1732 :param group_name:
1733 :type group_name:
1734 :param description:
1735 :type description:
1736 :param owner:
1737 :type owner:
1738 :param parent:
1739 :type parent:
1740 :param copy_permissions:
1741 :type copy_permissions:
1742
1743 OUTPUT::
1541 OUTPUT::
1744
1542
1745 id : <id_given_in_input>
1543 id : <id_given_in_input>
1746 result : {
1544 result : {
1747 "msg" : "created new repo group `<repo_group_name>`",
1545 "msg" : "created new repo group `<repo_group_name>`",
1748 "repo_group" : <repogroup_object>
1546 "repo_group" : <repogroup_object>
1749 }
1547 }
1750 error : null
1548 error : null
1751
1549
1752 ERROR OUTPUT::
1550 ERROR OUTPUT::
1753
1551
1754 id : <id_given_in_input>
1552 id : <id_given_in_input>
1755 result : null
1553 result : null
1756 error : {
1554 error : {
1757 failed to create repo group `<repogroupid>`
1555 failed to create repo group `<repogroupid>`
1758 }
1556 }
1759 """
1557 """
1760 if db.RepoGroup.get_by_group_name(group_name):
1558 if db.RepoGroup.get_by_group_name(group_name):
1761 raise JSONRPCError("repo group `%s` already exist" % (group_name,))
1559 raise JSONRPCError("repo group `%s` already exist" % (group_name,))
1762
1560
1763 if owner is None:
1561 if owner is None:
1764 owner = request.authuser.user_id
1562 owner = request.authuser.user_id
1765 group_description = description
1563 group_description = description
1766 parent_group = None
1564 parent_group = None
1767 if parent is not None:
1565 if parent is not None:
1768 parent_group = get_repo_group_or_error(parent)
1566 parent_group = get_repo_group_or_error(parent)
1769
1567
1770 try:
1568 try:
1771 repo_group = RepoGroupModel().create(
1569 repo_group = RepoGroupModel().create(
1772 group_name=group_name,
1570 group_name=group_name,
1773 group_description=group_description,
1571 group_description=group_description,
1774 owner=owner,
1572 owner=owner,
1775 parent=parent_group,
1573 parent=parent_group,
1776 copy_permissions=copy_permissions
1574 copy_permissions=copy_permissions
1777 )
1575 )
1778 meta.Session().commit()
1576 meta.Session().commit()
1779 return dict(
1577 return dict(
1780 msg='created new repo group `%s`' % group_name,
1578 msg='created new repo group `%s`' % group_name,
1781 repo_group=repo_group.get_api_data()
1579 repo_group=repo_group.get_api_data()
1782 )
1580 )
1783 except Exception:
1581 except Exception:
1784
1582
1785 log.error(traceback.format_exc())
1583 log.error(traceback.format_exc())
1786 raise JSONRPCError('failed to create repo group `%s`' % (group_name,))
1584 raise JSONRPCError('failed to create repo group `%s`' % (group_name,))
1787
1585
1788 @HasPermissionAnyDecorator('hg.admin')
1586 @HasPermissionAnyDecorator('hg.admin')
1789 def update_repo_group(self, repogroupid, group_name=None,
1587 def update_repo_group(self, repogroupid, group_name=None,
1790 description=None,
1588 description=None,
1791 owner=None,
1589 owner=None,
1792 parent=None):
1590 parent=None):
1793 repo_group = get_repo_group_or_error(repogroupid)
1591 repo_group = get_repo_group_or_error(repogroupid)
1794 parent_repo_group_id = None if parent is None else get_repo_group_or_error(parent).group_id
1592 parent_repo_group_id = None if parent is None else get_repo_group_or_error(parent).group_id
1795
1593
1796 updates = {}
1594 updates = {}
1797 try:
1595 try:
1798 store_update(updates, group_name, 'group_name')
1596 store_update(updates, group_name, 'group_name')
1799 store_update(updates, description, 'group_description')
1597 store_update(updates, description, 'group_description')
1800 store_update(updates, owner, 'owner')
1598 store_update(updates, owner, 'owner')
1801 store_update(updates, parent_repo_group_id, 'parent_group_id')
1599 store_update(updates, parent_repo_group_id, 'parent_group_id')
1802 repo_group = RepoGroupModel().update(repo_group, updates)
1600 repo_group = RepoGroupModel().update(repo_group, updates)
1803 meta.Session().commit()
1601 meta.Session().commit()
1804 return dict(
1602 return dict(
1805 msg='updated repository group ID:%s %s' % (repo_group.group_id,
1603 msg='updated repository group ID:%s %s' % (repo_group.group_id,
1806 repo_group.group_name),
1604 repo_group.group_name),
1807 repo_group=repo_group.get_api_data()
1605 repo_group=repo_group.get_api_data()
1808 )
1606 )
1809 except Exception:
1607 except Exception:
1810 log.error(traceback.format_exc())
1608 log.error(traceback.format_exc())
1811 raise JSONRPCError('failed to update repository group `%s`'
1609 raise JSONRPCError('failed to update repository group `%s`'
1812 % (repogroupid,))
1610 % (repogroupid,))
1813
1611
1814 @HasPermissionAnyDecorator('hg.admin')
1612 @HasPermissionAnyDecorator('hg.admin')
1815 def delete_repo_group(self, repogroupid):
1613 def delete_repo_group(self, repogroupid):
1816 """
1614 """
1817 :param repogroupid: name or id of repository group
1818 :type repogroupid: str or int
1819
1820 OUTPUT::
1615 OUTPUT::
1821
1616
1822 id : <id_given_in_input>
1617 id : <id_given_in_input>
1823 result : {
1618 result : {
1824 'msg' : 'deleted repo group ID:<repogroupid> <repogroupname>
1619 'msg' : 'deleted repo group ID:<repogroupid> <repogroupname>
1825 'repo_group' : null
1620 'repo_group' : null
1826 }
1621 }
1827 error : null
1622 error : null
1828
1623
1829 ERROR OUTPUT::
1624 ERROR OUTPUT::
1830
1625
1831 id : <id_given_in_input>
1626 id : <id_given_in_input>
1832 result : null
1627 result : null
1833 error : {
1628 error : {
1834 "failed to delete repo group ID:<repogroupid> <repogroupname>"
1629 "failed to delete repo group ID:<repogroupid> <repogroupname>"
1835 }
1630 }
1836 """
1631 """
1837 repo_group = get_repo_group_or_error(repogroupid)
1632 repo_group = get_repo_group_or_error(repogroupid)
1838
1633
1839 try:
1634 try:
1840 RepoGroupModel().delete(repo_group)
1635 RepoGroupModel().delete(repo_group)
1841 meta.Session().commit()
1636 meta.Session().commit()
1842 return dict(
1637 return dict(
1843 msg='deleted repo group ID:%s %s' %
1638 msg='deleted repo group ID:%s %s' %
1844 (repo_group.group_id, repo_group.group_name),
1639 (repo_group.group_id, repo_group.group_name),
1845 repo_group=None
1640 repo_group=None
1846 )
1641 )
1847 except Exception:
1642 except Exception:
1848 log.error(traceback.format_exc())
1643 log.error(traceback.format_exc())
1849 raise JSONRPCError('failed to delete repo group ID:%s %s' %
1644 raise JSONRPCError('failed to delete repo group ID:%s %s' %
1850 (repo_group.group_id, repo_group.group_name)
1645 (repo_group.group_id, repo_group.group_name)
1851 )
1646 )
1852
1647
1853 # permission check inside
1648 # permission check inside
1854 def grant_user_permission_to_repo_group(self, repogroupid, userid,
1649 def grant_user_permission_to_repo_group(self, repogroupid, userid,
1855 perm, apply_to_children='none'):
1650 perm, apply_to_children='none'):
1856 """
1651 """
1857 Grant permission for user on given repository group, or update existing
1652 Grant permission for user on given repository group, or update existing
1858 one if found. This command can be executed only using api_key belonging
1653 one if found. This command can be executed only using api_key belonging
1859 to user with admin rights, or user who has admin right to given repository
1654 to user with admin rights, or user who has admin right to given repository
1860 group.
1655 group.
1861
1656
1862 :param repogroupid: name or id of repository group
1863 :type repogroupid: str or int
1864 :param userid:
1865 :param perm: (group.(none|read|write|admin))
1866 :type perm: str
1867 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1868 :type apply_to_children: str
1869
1870 OUTPUT::
1657 OUTPUT::
1871
1658
1872 id : <id_given_in_input>
1659 id : <id_given_in_input>
1873 result : {
1660 result : {
1874 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1661 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1875 "success" : true
1662 "success" : true
1876 }
1663 }
1877 error : null
1664 error : null
1878
1665
1879 ERROR OUTPUT::
1666 ERROR OUTPUT::
1880
1667
1881 id : <id_given_in_input>
1668 id : <id_given_in_input>
1882 result : null
1669 result : null
1883 error : {
1670 error : {
1884 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1671 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1885 }
1672 }
1886 """
1673 """
1887 repo_group = get_repo_group_or_error(repogroupid)
1674 repo_group = get_repo_group_or_error(repogroupid)
1888
1675
1889 if not HasPermissionAny('hg.admin')():
1676 if not HasPermissionAny('hg.admin')():
1890 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1677 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1891 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1678 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1892
1679
1893 user = get_user_or_error(userid)
1680 user = get_user_or_error(userid)
1894 perm = get_perm_or_error(perm, prefix='group.')
1681 perm = get_perm_or_error(perm, prefix='group.')
1895
1682
1896 try:
1683 try:
1897 RepoGroupModel().add_permission(repo_group=repo_group,
1684 RepoGroupModel().add_permission(repo_group=repo_group,
1898 obj=user,
1685 obj=user,
1899 obj_type="user",
1686 obj_type="user",
1900 perm=perm,
1687 perm=perm,
1901 recursive=apply_to_children)
1688 recursive=apply_to_children)
1902 meta.Session().commit()
1689 meta.Session().commit()
1903 return dict(
1690 return dict(
1904 msg='Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1691 msg='Granted perm: `%s` (recursive:%s) for user: `%s` in repo group: `%s`' % (
1905 perm.permission_name, apply_to_children, user.username, repo_group.name
1692 perm.permission_name, apply_to_children, user.username, repo_group.name
1906 ),
1693 ),
1907 success=True
1694 success=True
1908 )
1695 )
1909 except Exception:
1696 except Exception:
1910 log.error(traceback.format_exc())
1697 log.error(traceback.format_exc())
1911 raise JSONRPCError(
1698 raise JSONRPCError(
1912 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1699 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1913 userid, repo_group.name))
1700 userid, repo_group.name))
1914
1701
1915 # permission check inside
1702 # permission check inside
1916 def revoke_user_permission_from_repo_group(self, repogroupid, userid,
1703 def revoke_user_permission_from_repo_group(self, repogroupid, userid,
1917 apply_to_children='none'):
1704 apply_to_children='none'):
1918 """
1705 """
1919 Revoke permission for user on given repository group. This command can
1706 Revoke permission for user on given repository group. This command can
1920 be executed only using api_key belonging to user with admin rights, or
1707 be executed only using api_key belonging to user with admin rights, or
1921 user who has admin right to given repository group.
1708 user who has admin right to given repository group.
1922
1709
1923 :param repogroupid: name or id of repository group
1924 :type repogroupid: str or int
1925 :param userid:
1926 :type userid:
1927 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1928 :type apply_to_children: str
1929
1930 OUTPUT::
1710 OUTPUT::
1931
1711
1932 id : <id_given_in_input>
1712 id : <id_given_in_input>
1933 result : {
1713 result : {
1934 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1714 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
1935 "success" : true
1715 "success" : true
1936 }
1716 }
1937 error : null
1717 error : null
1938
1718
1939 ERROR OUTPUT::
1719 ERROR OUTPUT::
1940
1720
1941 id : <id_given_in_input>
1721 id : <id_given_in_input>
1942 result : null
1722 result : null
1943 error : {
1723 error : {
1944 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1724 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
1945 }
1725 }
1946 """
1726 """
1947 repo_group = get_repo_group_or_error(repogroupid)
1727 repo_group = get_repo_group_or_error(repogroupid)
1948
1728
1949 if not HasPermissionAny('hg.admin')():
1729 if not HasPermissionAny('hg.admin')():
1950 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1730 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1951 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1731 raise JSONRPCError('repository group `%s` does not exist' % (repogroupid,))
1952
1732
1953 user = get_user_or_error(userid)
1733 user = get_user_or_error(userid)
1954
1734
1955 try:
1735 try:
1956 RepoGroupModel().delete_permission(repo_group=repo_group,
1736 RepoGroupModel().delete_permission(repo_group=repo_group,
1957 obj=user,
1737 obj=user,
1958 obj_type="user",
1738 obj_type="user",
1959 recursive=apply_to_children)
1739 recursive=apply_to_children)
1960
1740
1961 meta.Session().commit()
1741 meta.Session().commit()
1962 return dict(
1742 return dict(
1963 msg='Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
1743 msg='Revoked perm (recursive:%s) for user: `%s` in repo group: `%s`' % (
1964 apply_to_children, user.username, repo_group.name
1744 apply_to_children, user.username, repo_group.name
1965 ),
1745 ),
1966 success=True
1746 success=True
1967 )
1747 )
1968 except Exception:
1748 except Exception:
1969 log.error(traceback.format_exc())
1749 log.error(traceback.format_exc())
1970 raise JSONRPCError(
1750 raise JSONRPCError(
1971 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1751 'failed to edit permission for user: `%s` in repo group: `%s`' % (
1972 userid, repo_group.name))
1752 userid, repo_group.name))
1973
1753
1974 # permission check inside
1754 # permission check inside
1975 def grant_user_group_permission_to_repo_group(
1755 def grant_user_group_permission_to_repo_group(
1976 self, repogroupid, usergroupid, perm,
1756 self, repogroupid, usergroupid, perm,
1977 apply_to_children='none'):
1757 apply_to_children='none'):
1978 """
1758 """
1979 Grant permission for user group on given repository group, or update
1759 Grant permission for user group on given repository group, or update
1980 existing one if found. This command can be executed only using
1760 existing one if found. This command can be executed only using
1981 api_key belonging to user with admin rights, or user who has admin
1761 api_key belonging to user with admin rights, or user who has admin
1982 right to given repository group.
1762 right to given repository group.
1983
1763
1984 :param repogroupid: name or id of repository group
1985 :type repogroupid: str or int
1986 :param usergroupid: id of usergroup
1987 :type usergroupid: str or int
1988 :param perm: (group.(none|read|write|admin))
1989 :type perm: str
1990 :param apply_to_children: 'none', 'repos', 'groups', 'all'
1991 :type apply_to_children: str
1992
1993 OUTPUT::
1764 OUTPUT::
1994
1765
1995 id : <id_given_in_input>
1766 id : <id_given_in_input>
1996 result : {
1767 result : {
1997 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
1768 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
1998 "success" : true
1769 "success" : true
1999 }
1770 }
2000 error : null
1771 error : null
2001
1772
2002 ERROR OUTPUT::
1773 ERROR OUTPUT::
2003
1774
2004 id : <id_given_in_input>
1775 id : <id_given_in_input>
2005 result : null
1776 result : null
2006 error : {
1777 error : {
2007 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
1778 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2008 }
1779 }
2009 """
1780 """
2010 repo_group = get_repo_group_or_error(repogroupid)
1781 repo_group = get_repo_group_or_error(repogroupid)
2011 perm = get_perm_or_error(perm, prefix='group.')
1782 perm = get_perm_or_error(perm, prefix='group.')
2012 user_group = get_user_group_or_error(usergroupid)
1783 user_group = get_user_group_or_error(usergroupid)
2013 if not HasPermissionAny('hg.admin')():
1784 if not HasPermissionAny('hg.admin')():
2014 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1785 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
2015 raise JSONRPCError(
1786 raise JSONRPCError(
2016 'repository group `%s` does not exist' % (repogroupid,))
1787 'repository group `%s` does not exist' % (repogroupid,))
2017
1788
2018 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1789 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
2019 raise JSONRPCError(
1790 raise JSONRPCError(
2020 'user group `%s` does not exist' % (usergroupid,))
1791 'user group `%s` does not exist' % (usergroupid,))
2021
1792
2022 try:
1793 try:
2023 RepoGroupModel().add_permission(repo_group=repo_group,
1794 RepoGroupModel().add_permission(repo_group=repo_group,
2024 obj=user_group,
1795 obj=user_group,
2025 obj_type="user_group",
1796 obj_type="user_group",
2026 perm=perm,
1797 perm=perm,
2027 recursive=apply_to_children)
1798 recursive=apply_to_children)
2028 meta.Session().commit()
1799 meta.Session().commit()
2029 return dict(
1800 return dict(
2030 msg='Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
1801 msg='Granted perm: `%s` (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2031 perm.permission_name, apply_to_children,
1802 perm.permission_name, apply_to_children,
2032 user_group.users_group_name, repo_group.name
1803 user_group.users_group_name, repo_group.name
2033 ),
1804 ),
2034 success=True
1805 success=True
2035 )
1806 )
2036 except Exception:
1807 except Exception:
2037 log.error(traceback.format_exc())
1808 log.error(traceback.format_exc())
2038 raise JSONRPCError(
1809 raise JSONRPCError(
2039 'failed to edit permission for user group: `%s` in '
1810 'failed to edit permission for user group: `%s` in '
2040 'repo group: `%s`' % (
1811 'repo group: `%s`' % (
2041 usergroupid, repo_group.name
1812 usergroupid, repo_group.name
2042 )
1813 )
2043 )
1814 )
2044
1815
2045 # permission check inside
1816 # permission check inside
2046 def revoke_user_group_permission_from_repo_group(
1817 def revoke_user_group_permission_from_repo_group(
2047 self, repogroupid, usergroupid,
1818 self, repogroupid, usergroupid,
2048 apply_to_children='none'):
1819 apply_to_children='none'):
2049 """
1820 """
2050 Revoke permission for user group on given repository. This command can be
1821 Revoke permission for user group on given repository. This command can be
2051 executed only using api_key belonging to user with admin rights, or
1822 executed only using api_key belonging to user with admin rights, or
2052 user who has admin right to given repository group.
1823 user who has admin right to given repository group.
2053
1824
2054 :param repogroupid: name or id of repository group
2055 :type repogroupid: str or int
2056 :param usergroupid:
2057 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2058 :type apply_to_children: str
2059
2060 OUTPUT::
1825 OUTPUT::
2061
1826
2062 id : <id_given_in_input>
1827 id : <id_given_in_input>
2063 result : {
1828 result : {
2064 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
1829 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
2065 "success" : true
1830 "success" : true
2066 }
1831 }
2067 error : null
1832 error : null
2068
1833
2069 ERROR OUTPUT::
1834 ERROR OUTPUT::
2070
1835
2071 id : <id_given_in_input>
1836 id : <id_given_in_input>
2072 result : null
1837 result : null
2073 error : {
1838 error : {
2074 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
1839 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2075 }
1840 }
2076 """
1841 """
2077 repo_group = get_repo_group_or_error(repogroupid)
1842 repo_group = get_repo_group_or_error(repogroupid)
2078 user_group = get_user_group_or_error(usergroupid)
1843 user_group = get_user_group_or_error(usergroupid)
2079 if not HasPermissionAny('hg.admin')():
1844 if not HasPermissionAny('hg.admin')():
2080 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
1845 if not HasRepoGroupPermissionLevel('admin')(repo_group.group_name):
2081 raise JSONRPCError(
1846 raise JSONRPCError(
2082 'repository group `%s` does not exist' % (repogroupid,))
1847 'repository group `%s` does not exist' % (repogroupid,))
2083
1848
2084 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
1849 if not HasUserGroupPermissionLevel('read')(user_group.users_group_name):
2085 raise JSONRPCError(
1850 raise JSONRPCError(
2086 'user group `%s` does not exist' % (usergroupid,))
1851 'user group `%s` does not exist' % (usergroupid,))
2087
1852
2088 try:
1853 try:
2089 RepoGroupModel().delete_permission(repo_group=repo_group,
1854 RepoGroupModel().delete_permission(repo_group=repo_group,
2090 obj=user_group,
1855 obj=user_group,
2091 obj_type="user_group",
1856 obj_type="user_group",
2092 recursive=apply_to_children)
1857 recursive=apply_to_children)
2093 meta.Session().commit()
1858 meta.Session().commit()
2094 return dict(
1859 return dict(
2095 msg='Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
1860 msg='Revoked perm (recursive:%s) for user group: `%s` in repo group: `%s`' % (
2096 apply_to_children, user_group.users_group_name, repo_group.name
1861 apply_to_children, user_group.users_group_name, repo_group.name
2097 ),
1862 ),
2098 success=True
1863 success=True
2099 )
1864 )
2100 except Exception:
1865 except Exception:
2101 log.error(traceback.format_exc())
1866 log.error(traceback.format_exc())
2102 raise JSONRPCError(
1867 raise JSONRPCError(
2103 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
1868 'failed to edit permission for user group: `%s` in repo group: `%s`' % (
2104 user_group.users_group_name, repo_group.name
1869 user_group.users_group_name, repo_group.name
2105 )
1870 )
2106 )
1871 )
2107
1872
2108 def get_gist(self, gistid):
1873 def get_gist(self, gistid):
2109 """
1874 """
2110 Get given gist by id
1875 Get given gist by id
2111
2112 :param gistid: id of private or public gist
2113 :type gistid: str
2114 """
1876 """
2115 gist = get_gist_or_error(gistid)
1877 gist = get_gist_or_error(gistid)
2116 if not HasPermissionAny('hg.admin')():
1878 if not HasPermissionAny('hg.admin')():
2117 if gist.owner_id != request.authuser.user_id:
1879 if gist.owner_id != request.authuser.user_id:
2118 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
1880 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
2119 return gist.get_api_data()
1881 return gist.get_api_data()
2120
1882
2121 def get_gists(self, userid=None):
1883 def get_gists(self, userid=None):
2122 """
1884 """
2123 Get all gists for given user. If userid is empty returned gists
1885 Get all gists for given user. If userid is empty returned gists
2124 are for user who called the api
1886 are for user who called the api
2125
2126 :param userid: user to get gists for
2127 :type userid: Optional(str or int)
2128 """
1887 """
2129 if not HasPermissionAny('hg.admin')():
1888 if not HasPermissionAny('hg.admin')():
2130 # make sure normal user does not pass someone else userid,
1889 # make sure normal user does not pass someone else userid,
2131 # he is not allowed to do that
1890 # he is not allowed to do that
2132 if userid is not None and userid != request.authuser.user_id:
1891 if userid is not None and userid != request.authuser.user_id:
2133 raise JSONRPCError(
1892 raise JSONRPCError(
2134 'userid is not the same as your user'
1893 'userid is not the same as your user'
2135 )
1894 )
2136
1895
2137 if userid is None:
1896 if userid is None:
2138 user_id = request.authuser.user_id
1897 user_id = request.authuser.user_id
2139 else:
1898 else:
2140 user_id = get_user_or_error(userid).user_id
1899 user_id = get_user_or_error(userid).user_id
2141
1900
2142 return [
1901 return [
2143 gist.get_api_data()
1902 gist.get_api_data()
2144 for gist in db.Gist().query()
1903 for gist in db.Gist().query()
2145 .filter_by(is_expired=False)
1904 .filter_by(is_expired=False)
2146 .filter(db.Gist.owner_id == user_id)
1905 .filter(db.Gist.owner_id == user_id)
2147 .order_by(db.Gist.created_on.desc())
1906 .order_by(db.Gist.created_on.desc())
2148 ]
1907 ]
2149
1908
2150 def create_gist(self, files, owner=None,
1909 def create_gist(self, files, owner=None,
2151 gist_type=db.Gist.GIST_PUBLIC, lifetime=-1,
1910 gist_type=db.Gist.GIST_PUBLIC, lifetime=-1,
2152 description=''):
1911 description=''):
2153 """
1912 """
2154 Creates new Gist
1913 Creates new Gist
2155
1914
2156 :param files: files to be added to gist
2157 {'filename': {'content':'...', 'lexer': null},
2158 'filename2': {'content':'...', 'lexer': null}}
2159 :type files: dict
2160 :param owner: gist owner, defaults to api method caller
2161 :type owner: Optional(str or int)
2162 :param gist_type: type of gist 'public' or 'private'
2163 :type gist_type: Optional(str)
2164 :param lifetime: time in minutes of gist lifetime
2165 :type lifetime: Optional(int)
2166 :param description: gist description
2167 :type description: str
2168
2169 OUTPUT::
1915 OUTPUT::
2170
1916
2171 id : <id_given_in_input>
1917 id : <id_given_in_input>
2172 result : {
1918 result : {
2173 "msg" : "created new gist",
1919 "msg" : "created new gist",
2174 "gist" : <gist_object>
1920 "gist" : <gist_object>
2175 }
1921 }
2176 error : null
1922 error : null
2177
1923
2178 ERROR OUTPUT::
1924 ERROR OUTPUT::
2179
1925
2180 id : <id_given_in_input>
1926 id : <id_given_in_input>
2181 result : null
1927 result : null
2182 error : {
1928 error : {
2183 "failed to create gist"
1929 "failed to create gist"
2184 }
1930 }
2185 """
1931 """
2186 try:
1932 try:
2187 if owner is None:
1933 if owner is None:
2188 owner = request.authuser.user_id
1934 owner = request.authuser.user_id
2189
1935
2190 owner = get_user_or_error(owner)
1936 owner = get_user_or_error(owner)
2191
1937
2192 gist = GistModel().create(description=description,
1938 gist = GistModel().create(description=description,
2193 owner=owner,
1939 owner=owner,
2194 ip_addr=request.ip_addr,
1940 ip_addr=request.ip_addr,
2195 gist_mapping=files,
1941 gist_mapping=files,
2196 gist_type=gist_type,
1942 gist_type=gist_type,
2197 lifetime=lifetime)
1943 lifetime=lifetime)
2198 meta.Session().commit()
1944 meta.Session().commit()
2199 return dict(
1945 return dict(
2200 msg='created new gist',
1946 msg='created new gist',
2201 gist=gist.get_api_data()
1947 gist=gist.get_api_data()
2202 )
1948 )
2203 except Exception:
1949 except Exception:
2204 log.error(traceback.format_exc())
1950 log.error(traceback.format_exc())
2205 raise JSONRPCError('failed to create gist')
1951 raise JSONRPCError('failed to create gist')
2206
1952
2207 # permission check inside
1953 # permission check inside
2208 def delete_gist(self, gistid):
1954 def delete_gist(self, gistid):
2209 """
1955 """
2210 Deletes existing gist
1956 Deletes existing gist
2211
1957
2212 :param gistid: id of gist to delete
2213 :type gistid: str
2214
2215 OUTPUT::
1958 OUTPUT::
2216
1959
2217 id : <id_given_in_input>
1960 id : <id_given_in_input>
2218 result : {
1961 result : {
2219 "msg" : "deleted gist ID: <gist_id>",
1962 "msg" : "deleted gist ID: <gist_id>",
2220 "gist" : null
1963 "gist" : null
2221 }
1964 }
2222 error : null
1965 error : null
2223
1966
2224 ERROR OUTPUT::
1967 ERROR OUTPUT::
2225
1968
2226 id : <id_given_in_input>
1969 id : <id_given_in_input>
2227 result : null
1970 result : null
2228 error : {
1971 error : {
2229 "failed to delete gist ID:<gist_id>"
1972 "failed to delete gist ID:<gist_id>"
2230 }
1973 }
2231 """
1974 """
2232 gist = get_gist_or_error(gistid)
1975 gist = get_gist_or_error(gistid)
2233 if not HasPermissionAny('hg.admin')():
1976 if not HasPermissionAny('hg.admin')():
2234 if gist.owner_id != request.authuser.user_id:
1977 if gist.owner_id != request.authuser.user_id:
2235 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
1978 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
2236
1979
2237 try:
1980 try:
2238 GistModel().delete(gist)
1981 GistModel().delete(gist)
2239 meta.Session().commit()
1982 meta.Session().commit()
2240 return dict(
1983 return dict(
2241 msg='deleted gist ID:%s' % (gist.gist_access_id,),
1984 msg='deleted gist ID:%s' % (gist.gist_access_id,),
2242 gist=None
1985 gist=None
2243 )
1986 )
2244 except Exception:
1987 except Exception:
2245 log.error(traceback.format_exc())
1988 log.error(traceback.format_exc())
2246 raise JSONRPCError('failed to delete gist ID:%s'
1989 raise JSONRPCError('failed to delete gist ID:%s'
2247 % (gist.gist_access_id,))
1990 % (gist.gist_access_id,))
2248
1991
2249 # permission check inside
1992 # permission check inside
2250 def get_changesets(self, repoid, start=None, end=None, start_date=None,
1993 def get_changesets(self, repoid, start=None, end=None, start_date=None,
2251 end_date=None, branch_name=None, reverse=False, with_file_list=False, max_revisions=None):
1994 end_date=None, branch_name=None, reverse=False, with_file_list=False, max_revisions=None):
2252 repo = get_repo_or_error(repoid)
1995 repo = get_repo_or_error(repoid)
2253 if not HasRepoPermissionLevel('read')(repo.repo_name):
1996 if not HasRepoPermissionLevel('read')(repo.repo_name):
2254 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
1997 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
2255
1998
2256 format = "%Y-%m-%dT%H:%M:%S"
1999 format = "%Y-%m-%dT%H:%M:%S"
2257 try:
2000 try:
2258 return [e.__json__(with_file_list) for e in
2001 return [e.__json__(with_file_list) for e in
2259 repo.scm_instance.get_changesets(start,
2002 repo.scm_instance.get_changesets(start,
2260 end,
2003 end,
2261 datetime.strptime(start_date, format) if start_date else None,
2004 datetime.strptime(start_date, format) if start_date else None,
2262 datetime.strptime(end_date, format) if end_date else None,
2005 datetime.strptime(end_date, format) if end_date else None,
2263 branch_name,
2006 branch_name,
2264 reverse, max_revisions)]
2007 reverse, max_revisions)]
2265 except EmptyRepositoryError as e:
2008 except EmptyRepositoryError as e:
2266 raise JSONRPCError('Repository is empty')
2009 raise JSONRPCError('Repository is empty')
2267
2010
2268 # permission check inside
2011 # permission check inside
2269 def get_changeset(self, repoid, raw_id, with_reviews=False):
2012 def get_changeset(self, repoid, raw_id, with_reviews=False):
2270 repo = get_repo_or_error(repoid)
2013 repo = get_repo_or_error(repoid)
2271 if not HasRepoPermissionLevel('read')(repo.repo_name):
2014 if not HasRepoPermissionLevel('read')(repo.repo_name):
2272 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
2015 raise JSONRPCError('Access denied to repo %s' % repo.repo_name)
2273 changeset = repo.get_changeset(raw_id)
2016 changeset = repo.get_changeset(raw_id)
2274 if isinstance(changeset, EmptyChangeset):
2017 if isinstance(changeset, EmptyChangeset):
2275 raise JSONRPCError('Changeset %s does not exist' % raw_id)
2018 raise JSONRPCError('Changeset %s does not exist' % raw_id)
2276
2019
2277 info = dict(changeset.as_dict())
2020 info = dict(changeset.as_dict())
2278
2021
2279 if with_reviews:
2022 if with_reviews:
2280 reviews = ChangesetStatusModel().get_statuses(
2023 reviews = ChangesetStatusModel().get_statuses(
2281 repo.repo_name, raw_id)
2024 repo.repo_name, raw_id)
2282 info["reviews"] = reviews
2025 info["reviews"] = reviews
2283
2026
2284 return info
2027 return info
2285
2028
2286 # permission check inside
2029 # permission check inside
2287 def get_pullrequest(self, pullrequest_id):
2030 def get_pullrequest(self, pullrequest_id):
2288 """
2031 """
2289 Get given pull request by id
2032 Get given pull request by id
2290 """
2033 """
2291 pull_request = db.PullRequest.get(pullrequest_id)
2034 pull_request = db.PullRequest.get(pullrequest_id)
2292 if pull_request is None:
2035 if pull_request is None:
2293 raise JSONRPCError('pull request `%s` does not exist' % (pullrequest_id,))
2036 raise JSONRPCError('pull request `%s` does not exist' % (pullrequest_id,))
2294 if not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name):
2037 if not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name):
2295 raise JSONRPCError('not allowed')
2038 raise JSONRPCError('not allowed')
2296 return pull_request.get_api_data()
2039 return pull_request.get_api_data()
2297
2040
2298 # permission check inside
2041 # permission check inside
2299 def comment_pullrequest(self, pull_request_id, comment_msg='', status=None, close_pr=False):
2042 def comment_pullrequest(self, pull_request_id, comment_msg='', status=None, close_pr=False):
2300 """
2043 """
2301 Add comment, close and change status of pull request.
2044 Add comment, close and change status of pull request.
2302 """
2045 """
2303 apiuser = get_user_or_error(request.authuser.user_id)
2046 apiuser = get_user_or_error(request.authuser.user_id)
2304 pull_request = db.PullRequest.get(pull_request_id)
2047 pull_request = db.PullRequest.get(pull_request_id)
2305 if pull_request is None:
2048 if pull_request is None:
2306 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2049 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2307 if (not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name)):
2050 if (not HasRepoPermissionLevel('read')(pull_request.org_repo.repo_name)):
2308 raise JSONRPCError('No permission to add comment. User needs at least reading permissions'
2051 raise JSONRPCError('No permission to add comment. User needs at least reading permissions'
2309 ' to the source repository.')
2052 ' to the source repository.')
2310 owner = apiuser.user_id == pull_request.owner_id
2053 owner = apiuser.user_id == pull_request.owner_id
2311 reviewer = apiuser.user_id in [reviewer.user_id for reviewer in pull_request.reviewers]
2054 reviewer = apiuser.user_id in [reviewer.user_id for reviewer in pull_request.reviewers]
2312 if close_pr and not (apiuser.admin or owner):
2055 if close_pr and not (apiuser.admin or owner):
2313 raise JSONRPCError('No permission to close pull request. User needs to be admin or owner.')
2056 raise JSONRPCError('No permission to close pull request. User needs to be admin or owner.')
2314 if status and not (apiuser.admin or owner or reviewer):
2057 if status and not (apiuser.admin or owner or reviewer):
2315 raise JSONRPCError('No permission to change pull request status. User needs to be admin, owner or reviewer.')
2058 raise JSONRPCError('No permission to change pull request status. User needs to be admin, owner or reviewer.')
2316 if pull_request.is_closed():
2059 if pull_request.is_closed():
2317 raise JSONRPCError('pull request is already closed')
2060 raise JSONRPCError('pull request is already closed')
2318
2061
2319 comment = ChangesetCommentsModel().create(
2062 comment = ChangesetCommentsModel().create(
2320 text=comment_msg,
2063 text=comment_msg,
2321 repo=pull_request.org_repo.repo_id,
2064 repo=pull_request.org_repo.repo_id,
2322 author=apiuser.user_id,
2065 author=apiuser.user_id,
2323 pull_request=pull_request.pull_request_id,
2066 pull_request=pull_request.pull_request_id,
2324 f_path=None,
2067 f_path=None,
2325 line_no=None,
2068 line_no=None,
2326 status_change=db.ChangesetStatus.get_status_lbl(status),
2069 status_change=db.ChangesetStatus.get_status_lbl(status),
2327 closing_pr=close_pr
2070 closing_pr=close_pr
2328 )
2071 )
2329 userlog.action_logger(apiuser,
2072 userlog.action_logger(apiuser,
2330 'user_commented_pull_request:%s' % pull_request_id,
2073 'user_commented_pull_request:%s' % pull_request_id,
2331 pull_request.org_repo, request.ip_addr)
2074 pull_request.org_repo, request.ip_addr)
2332 if status:
2075 if status:
2333 ChangesetStatusModel().set_status(
2076 ChangesetStatusModel().set_status(
2334 pull_request.org_repo_id,
2077 pull_request.org_repo_id,
2335 status,
2078 status,
2336 apiuser.user_id,
2079 apiuser.user_id,
2337 comment,
2080 comment,
2338 pull_request=pull_request_id
2081 pull_request=pull_request_id
2339 )
2082 )
2340 if close_pr:
2083 if close_pr:
2341 PullRequestModel().close_pull_request(pull_request_id)
2084 PullRequestModel().close_pull_request(pull_request_id)
2342 userlog.action_logger(apiuser,
2085 userlog.action_logger(apiuser,
2343 'user_closed_pull_request:%s' % pull_request_id,
2086 'user_closed_pull_request:%s' % pull_request_id,
2344 pull_request.org_repo, request.ip_addr)
2087 pull_request.org_repo, request.ip_addr)
2345 meta.Session().commit()
2088 meta.Session().commit()
2346 return True
2089 return True
2347
2090
2348 # permission check inside
2091 # permission check inside
2349 def edit_reviewers(self, pull_request_id, add=None, remove=None):
2092 def edit_reviewers(self, pull_request_id, add=None, remove=None):
2350 """
2093 """
2351 Add and/or remove one or more reviewers to a pull request, by username
2094 Add and/or remove one or more reviewers to a pull request, by username
2352 or user ID. Reviewers are specified either as a single-user string or
2095 or user ID. Reviewers are specified either as a single-user string or
2353 as a JSON list of one or more strings.
2096 as a JSON list of one or more strings.
2354 """
2097 """
2355 if add is None and remove is None:
2098 if add is None and remove is None:
2356 raise JSONRPCError('''Invalid request. Neither 'add' nor 'remove' is specified.''')
2099 raise JSONRPCError('''Invalid request. Neither 'add' nor 'remove' is specified.''')
2357
2100
2358 pull_request = db.PullRequest.get(pull_request_id)
2101 pull_request = db.PullRequest.get(pull_request_id)
2359 if pull_request is None:
2102 if pull_request is None:
2360 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2103 raise JSONRPCError('pull request `%s` does not exist' % (pull_request_id,))
2361
2104
2362 apiuser = get_user_or_error(request.authuser.user_id)
2105 apiuser = get_user_or_error(request.authuser.user_id)
2363 is_owner = apiuser.user_id == pull_request.owner_id
2106 is_owner = apiuser.user_id == pull_request.owner_id
2364 is_repo_admin = HasRepoPermissionLevel('admin')(pull_request.other_repo.repo_name)
2107 is_repo_admin = HasRepoPermissionLevel('admin')(pull_request.other_repo.repo_name)
2365 if not (apiuser.admin or is_repo_admin or is_owner):
2108 if not (apiuser.admin or is_repo_admin or is_owner):
2366 raise JSONRPCError('No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.')
2109 raise JSONRPCError('No permission to edit reviewers of this pull request. User needs to be admin or pull request owner.')
2367 if pull_request.is_closed():
2110 if pull_request.is_closed():
2368 raise JSONRPCError('Cannot edit reviewers of a closed pull request.')
2111 raise JSONRPCError('Cannot edit reviewers of a closed pull request.')
2369
2112
2370 if not isinstance(add, list):
2113 if not isinstance(add, list):
2371 add = [add]
2114 add = [add]
2372 if not isinstance(remove, list):
2115 if not isinstance(remove, list):
2373 remove = [remove]
2116 remove = [remove]
2374
2117
2375 # look up actual user objects from given name or id. Bail out if unknown.
2118 # look up actual user objects from given name or id. Bail out if unknown.
2376 add_objs = set(get_user_or_error(user) for user in add if user is not None)
2119 add_objs = set(get_user_or_error(user) for user in add if user is not None)
2377 remove_objs = set(get_user_or_error(user) for user in remove if user is not None)
2120 remove_objs = set(get_user_or_error(user) for user in remove if user is not None)
2378
2121
2379 new_reviewers = redundant_reviewers = set()
2122 new_reviewers = redundant_reviewers = set()
2380 if add_objs:
2123 if add_objs:
2381 new_reviewers, redundant_reviewers = PullRequestModel().add_reviewers(apiuser, pull_request, add_objs)
2124 new_reviewers, redundant_reviewers = PullRequestModel().add_reviewers(apiuser, pull_request, add_objs)
2382 if remove_objs:
2125 if remove_objs:
2383 PullRequestModel().remove_reviewers(apiuser, pull_request, remove_objs)
2126 PullRequestModel().remove_reviewers(apiuser, pull_request, remove_objs)
2384
2127
2385 meta.Session().commit()
2128 meta.Session().commit()
2386
2129
2387 return {
2130 return {
2388 'added': [x.username for x in new_reviewers],
2131 'added': [x.username for x in new_reviewers],
2389 'already_present': [x.username for x in redundant_reviewers],
2132 'already_present': [x.username for x in redundant_reviewers],
2390 # NOTE: no explicit check that removed reviewers were actually present.
2133 # NOTE: no explicit check that removed reviewers were actually present.
2391 'removed': [x.username for x in remove_objs],
2134 'removed': [x.username for x in remove_objs],
2392 }
2135 }
General Comments 0
You need to be logged in to leave comments. Login now