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