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