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