##// END OF EJS Templates
API invalidate_cache function ref #733
marcink -
r3235:d6029dac beta
parent child Browse files
Show More
@@ -1,959 +1,981 b''
1 1 .. _api:
2 2
3 3 ===
4 4 API
5 5 ===
6 6
7 7
8 8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 9 There's a single schema for calling all api methods. API is implemented
10 10 with JSON protocol both ways. An url to send API request to RhodeCode is
11 11 <your_server>/_admin/api
12 12
13 13 API ACCESS FOR WEB VIEWS
14 14 ++++++++++++++++++++++++
15 15
16 16 API access can also be turned on for each web view in RhodeCode that is
17 17 decorated with `@LoginRequired` decorator. To enable API access simple change
18 18 the standard login decorator to `@LoginRequired(api_access=True)`.
19 19 After this change, a rhodecode view can be accessed without login by adding a
20 20 GET parameter `?api_key=<api_key>` to url. By default this is only
21 21 enabled on RSS/ATOM feed views.
22 22
23 23
24 24 API ACCESS
25 25 ++++++++++
26 26
27 27 All clients are required to send JSON-RPC spec JSON data::
28 28
29 29 {
30 30 "id:"<id>",
31 31 "api_key":"<api_key>",
32 32 "method":"<method_name>",
33 33 "args":{"<arg_key>":"<arg_val>"}
34 34 }
35 35
36 36 Example call for autopulling remotes repos using curl::
37 37 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
38 38
39 39 Simply provide
40 40 - *id* A value of any type, which is used to match the response with the request that it is replying to.
41 41 - *api_key* for access and permission validation.
42 42 - *method* is name of method to call
43 43 - *args* is an key:value list of arguments to pass to method
44 44
45 45 .. note::
46 46
47 47 api_key can be found in your user account page
48 48
49 49
50 50 RhodeCode API will return always a JSON-RPC response::
51 51
52 52 {
53 53 "id":<id>, # matching id sent by request
54 54 "result": "<result>"|null, # JSON formatted result, null if any errors
55 55 "error": "null"|<error_message> # JSON formatted error (if any)
56 56 }
57 57
58 58 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
59 59 calling api *error* key from response will contain failure description
60 60 and result will be null.
61 61
62 62
63 63 API CLIENT
64 64 ++++++++++
65 65
66 66 From version 1.4 RhodeCode adds a script that allows to easily
67 67 communicate with API. After installing RhodeCode a `rhodecode-api` script
68 68 will be available.
69 69
70 70 To get started quickly simply run::
71 71
72 72 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
73 73
74 74 This will create a file named .config in the directory you executed it storing
75 75 json config file with credentials. You can skip this step and always provide
76 76 both of the arguments to be able to communicate with server
77 77
78 78
79 79 after that simply run any api command for example get_repo::
80 80
81 81 rhodecode-api get_repo
82 82
83 83 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
84 84 rhodecode said:
85 85 {'error': 'Missing non optional `repoid` arg in JSON DATA',
86 86 'id': 75,
87 87 'result': None}
88 88
89 89 Ups looks like we forgot to add an argument
90 90
91 91 Let's try again now giving the repoid as parameters::
92 92
93 93 rhodecode-api get_repo repoid:rhodecode
94 94
95 95 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
96 96 rhodecode said:
97 97 {'error': None,
98 98 'id': 39,
99 99 'result': <json data...>}
100 100
101 101
102 102
103 103 API METHODS
104 104 +++++++++++
105 105
106 106
107 107 pull
108 108 ----
109 109
110 110 Pulls given repo from remote location. Can be used to automatically keep
111 111 remote repos up to date. This command can be executed only using api_key
112 112 belonging to user with admin rights
113 113
114 114 INPUT::
115 115
116 116 id : <id_for_response>
117 117 api_key : "<api_key>"
118 118 method : "pull"
119 119 args : {
120 120 "repoid" : "<reponame or repo_id>"
121 121 }
122 122
123 123 OUTPUT::
124 124
125 125 id : <id_given_in_input>
126 126 result : "Pulled from `<reponame>`"
127 127 error : null
128 128
129 129
130 130 rescan_repos
131 131 ------------
132 132
133 133 Dispatch rescan repositories action. If remove_obsolete is set
134 134 RhodeCode will delete repos that are in database but not in the filesystem.
135 135 This command can be executed only using api_key belonging to user with admin
136 136 rights.
137 137
138 138 INPUT::
139 139
140 140 id : <id_for_response>
141 141 api_key : "<api_key>"
142 142 method : "rescan_repos"
143 143 args : {
144 144 "remove_obsolete" : "<boolean = Optional(False)>"
145 145 }
146 146
147 147 OUTPUT::
148 148
149 149 id : <id_given_in_input>
150 150 result : "{'added': [<list of names of added repos>],
151 151 'removed': [<list of names of removed repos>]}"
152 152 error : null
153 153
154 154
155 invalidate_cache
156 ----------------
157
158 Invalidate cache for repository.
159 This command can be executed only using api_key belonging to user with admin
160 rights or regular user that have write or admin or write access to repository.
161
162 INPUT::
163
164 id : <id_for_response>
165 api_key : "<api_key>"
166 method : "invalidate_cache"
167 args : {
168 "repoid" : "<reponame or repo_id>"
169 }
170
171 OUTPUT::
172
173 id : <id_given_in_input>
174 result : "Cache for repository `<reponame>` was invalidated: invalidated cache keys: <list_of_cache_keys>"
175 error : null
176
155 177 lock
156 178 ----
157 179
158 180 Set locking state on given repository by given user. If userid param is skipped
159 181 , then it is set to id of user whos calling this method.
160 182 This command can be executed only using api_key belonging to user with admin
161 183 rights or regular user that have admin or write access to repository.
162 184
163 185 INPUT::
164 186
165 187 id : <id_for_response>
166 188 api_key : "<api_key>"
167 189 method : "lock"
168 190 args : {
169 191 "repoid" : "<reponame or repo_id>"
170 192 "userid" : "<user_id or username = Optional(=apiuser)>",
171 193 "locked" : "<bool true|false>"
172 194 }
173 195
174 196 OUTPUT::
175 197
176 198 id : <id_given_in_input>
177 199 result : "User `<username>` set lock state for repo `<reponame>` to `true|false`"
178 200 error : null
179 201
180 202
181 203 show_ip
182 204 -------
183 205
184 206 Shows IP address as seen from RhodeCode server, together with all
185 207 defined IP addresses for given user.
186 208 This command can be executed only using api_key belonging to user with admin
187 209 rights.
188 210
189 211 INPUT::
190 212
191 213 id : <id_for_response>
192 214 api_key : "<api_key>"
193 215 method : "show_ip"
194 216 args : {
195 217 "userid" : "<user_id or username>",
196 218 }
197 219
198 220 OUTPUT::
199 221
200 222 id : <id_given_in_input>
201 223 result : {
202 224 "ip_addr_server": <ip_from_clien>",
203 225 "user_ips": [
204 226 {
205 227 "ip_addr": "<ip_with_mask>",
206 228 "ip_range": ["<start_ip>", "<end_ip>"],
207 229 },
208 230 ...
209 231 ]
210 232 }
211 233
212 234 error : null
213 235
214 236
215 237 get_user
216 238 --------
217 239
218 240 Get's an user by username or user_id, Returns empty result if user is not found.
219 241 If userid param is skipped it is set to id of user who is calling this method.
220 242 This command can be executed only using api_key belonging to user with admin
221 243 rights, or regular users that cannot specify different userid than theirs
222 244
223 245
224 246 INPUT::
225 247
226 248 id : <id_for_response>
227 249 api_key : "<api_key>"
228 250 method : "get_user"
229 251 args : {
230 252 "userid" : "<username or user_id Optional(=apiuser)>"
231 253 }
232 254
233 255 OUTPUT::
234 256
235 257 id : <id_given_in_input>
236 258 result: None if user does not exist or
237 259 {
238 260 "user_id" : "<user_id>",
239 261 "api_key" : "<api_key>",
240 262 "username" : "<username>",
241 263 "firstname": "<firstname>",
242 264 "lastname" : "<lastname>",
243 265 "email" : "<email>",
244 266 "emails": "<list_of_all_additional_emails>",
245 267 "ip_addresses": "<list_of_ip_addresses_for_user>",
246 268 "active" : "<bool>",
247 269 "admin" :Β  "<bool>",
248 270 "ldap_dn" : "<ldap_dn>",
249 271 "last_login": "<last_login>",
250 272 "permissions": {
251 273 "global": ["hg.create.repository",
252 274 "repository.read",
253 275 "hg.register.manual_activate"],
254 276 "repositories": {"repo1": "repository.none"},
255 277 "repositories_groups": {"Group1": "group.read"}
256 278 },
257 279 }
258 280
259 281 error: null
260 282
261 283
262 284 get_users
263 285 ---------
264 286
265 287 Lists all existing users. This command can be executed only using api_key
266 288 belonging to user with admin rights.
267 289
268 290
269 291 INPUT::
270 292
271 293 id : <id_for_response>
272 294 api_key : "<api_key>"
273 295 method : "get_users"
274 296 args : { }
275 297
276 298 OUTPUT::
277 299
278 300 id : <id_given_in_input>
279 301 result: [
280 302 {
281 303 "user_id" : "<user_id>",
282 304 "username" : "<username>",
283 305 "firstname": "<firstname>",
284 306 "lastname" : "<lastname>",
285 307 "email" : "<email>",
286 308 "emails": "<list_of_all_additional_emails>",
287 309 "ip_addresses": "<list_of_ip_addresses_for_user>",
288 310 "active" : "<bool>",
289 311 "admin" :Β  "<bool>",
290 312 "ldap_dn" : "<ldap_dn>",
291 313 "last_login": "<last_login>",
292 314 },
293 315 …
294 316 ]
295 317 error: null
296 318
297 319
298 320 create_user
299 321 -----------
300 322
301 323 Creates new user. This command can
302 324 be executed only using api_key belonging to user with admin rights.
303 325
304 326
305 327 INPUT::
306 328
307 329 id : <id_for_response>
308 330 api_key : "<api_key>"
309 331 method : "create_user"
310 332 args : {
311 333 "username" : "<username>",
312 334 "email" : "<useremail>",
313 335 "password" : "<password>",
314 336 "firstname" : "<firstname> = Optional(None)",
315 337 "lastname" : "<lastname> = Optional(None)",
316 338 "active" : "<bool> = Optional(True)",
317 339 "admin" : "<bool> = Optional(False)",
318 340 "ldap_dn" : "<ldap_dn> = Optional(None)"
319 341 }
320 342
321 343 OUTPUT::
322 344
323 345 id : <id_given_in_input>
324 346 result: {
325 347 "msg" : "created new user `<username>`",
326 348 "user": {
327 349 "user_id" : "<user_id>",
328 350 "username" : "<username>",
329 351 "firstname": "<firstname>",
330 352 "lastname" : "<lastname>",
331 353 "email" : "<email>",
332 354 "emails": "<list_of_all_additional_emails>",
333 355 "active" : "<bool>",
334 356 "admin" :Β  "<bool>",
335 357 "ldap_dn" : "<ldap_dn>",
336 358 "last_login": "<last_login>",
337 359 },
338 360 }
339 361 error: null
340 362
341 363
342 364 update_user
343 365 -----------
344 366
345 367 updates given user if such user exists. This command can
346 368 be executed only using api_key belonging to user with admin rights.
347 369
348 370
349 371 INPUT::
350 372
351 373 id : <id_for_response>
352 374 api_key : "<api_key>"
353 375 method : "update_user"
354 376 args : {
355 377 "userid" : "<user_id or username>",
356 378 "username" : "<username> = Optional(None)",
357 379 "email" : "<useremail> = Optional(None)",
358 380 "password" : "<password> = Optional(None)",
359 381 "firstname" : "<firstname> = Optional(None)",
360 382 "lastname" : "<lastname> = Optional(None)",
361 383 "active" : "<bool> = Optional(None)",
362 384 "admin" : "<bool> = Optional(None)",
363 385 "ldap_dn" : "<ldap_dn> = Optional(None)"
364 386 }
365 387
366 388 OUTPUT::
367 389
368 390 id : <id_given_in_input>
369 391 result: {
370 392 "msg" : "updated user ID:<userid> <username>",
371 393 "user": {
372 394 "user_id" : "<user_id>",
373 395 "username" : "<username>",
374 396 "firstname": "<firstname>",
375 397 "lastname" : "<lastname>",
376 398 "email" : "<email>",
377 399 "emails": "<list_of_all_additional_emails>",
378 400 "active" : "<bool>",
379 401 "admin" :Β  "<bool>",
380 402 "ldap_dn" : "<ldap_dn>",
381 403 "last_login": "<last_login>",
382 404 },
383 405 }
384 406 error: null
385 407
386 408
387 409 delete_user
388 410 -----------
389 411
390 412
391 413 deletes givenuser if such user exists. This command can
392 414 be executed only using api_key belonging to user with admin rights.
393 415
394 416
395 417 INPUT::
396 418
397 419 id : <id_for_response>
398 420 api_key : "<api_key>"
399 421 method : "delete_user"
400 422 args : {
401 423 "userid" : "<user_id or username>",
402 424 }
403 425
404 426 OUTPUT::
405 427
406 428 id : <id_given_in_input>
407 429 result: {
408 430 "msg" : "deleted user ID:<userid> <username>",
409 431 "user": null
410 432 }
411 433 error: null
412 434
413 435
414 436 get_users_group
415 437 ---------------
416 438
417 439 Gets an existing users group. This command can be executed only using api_key
418 440 belonging to user with admin rights.
419 441
420 442
421 443 INPUT::
422 444
423 445 id : <id_for_response>
424 446 api_key : "<api_key>"
425 447 method : "get_users_group"
426 448 args : {
427 449 "usersgroupid" : "<users group id or name>"
428 450 }
429 451
430 452 OUTPUT::
431 453
432 454 id : <id_given_in_input>
433 455 result : None if group not exist
434 456 {
435 457 "users_group_id" : "<id>",
436 458 "group_name" : "<groupname>",
437 459 "active": "<bool>",
438 460 "members" : [
439 461 {
440 462 "user_id" : "<user_id>",
441 463 "username" : "<username>",
442 464 "firstname": "<firstname>",
443 465 "lastname" : "<lastname>",
444 466 "email" : "<email>",
445 467 "emails": "<list_of_all_additional_emails>",
446 468 "active" : "<bool>",
447 469 "admin" :Β  "<bool>",
448 470 "ldap_dn" : "<ldap_dn>",
449 471 "last_login": "<last_login>",
450 472 },
451 473 …
452 474 ]
453 475 }
454 476 error : null
455 477
456 478
457 479 get_users_groups
458 480 ----------------
459 481
460 482 Lists all existing users groups. This command can be executed only using
461 483 api_key belonging to user with admin rights.
462 484
463 485
464 486 INPUT::
465 487
466 488 id : <id_for_response>
467 489 api_key : "<api_key>"
468 490 method : "get_users_groups"
469 491 args : { }
470 492
471 493 OUTPUT::
472 494
473 495 id : <id_given_in_input>
474 496 result : [
475 497 {
476 498 "users_group_id" : "<id>",
477 499 "group_name" : "<groupname>",
478 500 "active": "<bool>",
479 501 },
480 502 …
481 503 ]
482 504 error : null
483 505
484 506
485 507 create_users_group
486 508 ------------------
487 509
488 510 Creates new users group. This command can be executed only using api_key
489 511 belonging to user with admin rights
490 512
491 513
492 514 INPUT::
493 515
494 516 id : <id_for_response>
495 517 api_key : "<api_key>"
496 518 method : "create_users_group"
497 519 args: {
498 520 "group_name": "<groupname>",
499 521 "active":"<bool> = Optional(True)"
500 522 }
501 523
502 524 OUTPUT::
503 525
504 526 id : <id_given_in_input>
505 527 result: {
506 528 "msg": "created new users group `<groupname>`",
507 529 "users_group": {
508 530 "users_group_id" : "<id>",
509 531 "group_name" : "<groupname>",
510 532 "active": "<bool>",
511 533 },
512 534 }
513 535 error: null
514 536
515 537
516 538 add_user_to_users_group
517 539 -----------------------
518 540
519 541 Adds a user to a users group. If user exists in that group success will be
520 542 `false`. This command can be executed only using api_key
521 543 belonging to user with admin rights
522 544
523 545
524 546 INPUT::
525 547
526 548 id : <id_for_response>
527 549 api_key : "<api_key>"
528 550 method : "add_user_users_group"
529 551 args: {
530 552 "usersgroupid" : "<users group id or name>",
531 553 "userid" : "<user_id or username>",
532 554 }
533 555
534 556 OUTPUT::
535 557
536 558 id : <id_given_in_input>
537 559 result: {
538 560 "success": True|False # depends on if member is in group
539 561 "msg": "added member `<username>` to users group `<groupname>` |
540 562 User is already in that group"
541 563 }
542 564 error: null
543 565
544 566
545 567 remove_user_from_users_group
546 568 ----------------------------
547 569
548 570 Removes a user from a users group. If user is not in given group success will
549 571 be `false`. This command can be executed only
550 572 using api_key belonging to user with admin rights
551 573
552 574
553 575 INPUT::
554 576
555 577 id : <id_for_response>
556 578 api_key : "<api_key>"
557 579 method : "remove_user_from_users_group"
558 580 args: {
559 581 "usersgroupid" : "<users group id or name>",
560 582 "userid" : "<user_id or username>",
561 583 }
562 584
563 585 OUTPUT::
564 586
565 587 id : <id_given_in_input>
566 588 result: {
567 589 "success": True|False, # depends on if member is in group
568 590 "msg": "removed member <username> from users group <groupname> |
569 591 User wasn't in group"
570 592 }
571 593 error: null
572 594
573 595
574 596 get_repo
575 597 --------
576 598
577 599 Gets an existing repository by it's name or repository_id. Members will return
578 600 either users_group or user associated to that repository. This command can be
579 601 executed only using api_key belonging to user with admin
580 602 rights or regular user that have at least read access to repository.
581 603
582 604
583 605 INPUT::
584 606
585 607 id : <id_for_response>
586 608 api_key : "<api_key>"
587 609 method : "get_repo"
588 610 args: {
589 611 "repoid" : "<reponame or repo_id>"
590 612 }
591 613
592 614 OUTPUT::
593 615
594 616 id : <id_given_in_input>
595 617 result: None if repository does not exist or
596 618 {
597 619 "repo_id" : "<repo_id>",
598 620 "repo_name" : "<reponame>"
599 621 "repo_type" : "<repo_type>",
600 622 "clone_uri" : "<clone_uri>",
601 623 "enable_downloads": "<bool>",
602 624 "enable_locking": "<bool>",
603 625 "enable_statistics": "<bool>",
604 626 "private": "<bool>",
605 627 "created_on" : "<date_time_created>",
606 628 "description" : "<description>",
607 629 "landing_rev": "<landing_rev>",
608 630 "last_changeset": {
609 631 "author": "<full_author>",
610 632 "date": "<date_time_of_commit>",
611 633 "message": "<commit_message>",
612 634 "raw_id": "<raw_id>",
613 635 "revision": "<numeric_revision>",
614 636 "short_id": "<short_id>"
615 637 }
616 638 "owner": "<repo_owner>",
617 639 "fork_of": "<name_of_fork_parent>",
618 640 "members" : [
619 641 {
620 642 "type": "user",
621 643 "user_id" : "<user_id>",
622 644 "username" : "<username>",
623 645 "firstname": "<firstname>",
624 646 "lastname" : "<lastname>",
625 647 "email" : "<email>",
626 648 "emails": "<list_of_all_additional_emails>",
627 649 "active" : "<bool>",
628 650 "admin" :Β  "<bool>",
629 651 "ldap_dn" : "<ldap_dn>",
630 652 "last_login": "<last_login>",
631 653 "permission" : "repository.(read|write|admin)"
632 654 },
633 655 …
634 656 {
635 657 "type": "users_group",
636 658 "id" : "<usersgroupid>",
637 659 "name" : "<usersgroupname>",
638 660 "active": "<bool>",
639 661 "permission" : "repository.(read|write|admin)"
640 662 },
641 663 …
642 664 ]
643 665 "followers": [
644 666 {
645 667 "user_id" : "<user_id>",
646 668 "username" : "<username>",
647 669 "firstname": "<firstname>",
648 670 "lastname" : "<lastname>",
649 671 "email" : "<email>",
650 672 "emails": "<list_of_all_additional_emails>",
651 673 "ip_addresses": "<list_of_ip_addresses_for_user>",
652 674 "active" : "<bool>",
653 675 "admin" :Β  "<bool>",
654 676 "ldap_dn" : "<ldap_dn>",
655 677 "last_login": "<last_login>",
656 678 },
657 679 …
658 680 ]
659 681 }
660 682 error: null
661 683
662 684
663 685 get_repos
664 686 ---------
665 687
666 688 Lists all existing repositories. This command can be executed only using
667 689 api_key belonging to user with admin rights or regular user that have
668 690 admin, write or read access to repository.
669 691
670 692
671 693 INPUT::
672 694
673 695 id : <id_for_response>
674 696 api_key : "<api_key>"
675 697 method : "get_repos"
676 698 args: { }
677 699
678 700 OUTPUT::
679 701
680 702 id : <id_given_in_input>
681 703 result: [
682 704 {
683 705 "repo_id" : "<repo_id>",
684 706 "repo_name" : "<reponame>"
685 707 "repo_type" : "<repo_type>",
686 708 "clone_uri" : "<clone_uri>",
687 709 "private": : "<bool>",
688 710 "created_on" : "<datetimecreated>",
689 711 "description" : "<description>",
690 712 "landing_rev": "<landing_rev>",
691 713 "owner": "<repo_owner>",
692 714 "fork_of": "<name_of_fork_parent>",
693 715 "enable_downloads": "<bool>",
694 716 "enable_locking": "<bool>",
695 717 "enable_statistics": "<bool>",
696 718 },
697 719 …
698 720 ]
699 721 error: null
700 722
701 723
702 724 get_repo_nodes
703 725 --------------
704 726
705 727 returns a list of nodes and it's children in a flat list for a given path
706 728 at given revision. It's possible to specify ret_type to show only `files` or
707 729 `dirs`. This command can be executed only using api_key belonging to user
708 730 with admin rights
709 731
710 732
711 733 INPUT::
712 734
713 735 id : <id_for_response>
714 736 api_key : "<api_key>"
715 737 method : "get_repo_nodes"
716 738 args: {
717 739 "repoid" : "<reponame or repo_id>"
718 740 "revision" : "<revision>",
719 741 "root_path" : "<root_path>",
720 742 "ret_type" : "<ret_type> = Optional('all')"
721 743 }
722 744
723 745 OUTPUT::
724 746
725 747 id : <id_given_in_input>
726 748 result: [
727 749 {
728 750 "name" : "<name>"
729 751 "type" : "<type>",
730 752 },
731 753 …
732 754 ]
733 755 error: null
734 756
735 757
736 758 create_repo
737 759 -----------
738 760
739 761 Creates a repository. If repository name contains "/", all needed repository
740 762 groups will be created. For example "foo/bar/baz" will create groups
741 763 "foo", "bar" (with "foo" as parent), and create "baz" repository with
742 764 "bar" as group. This command can be executed only using api_key belonging to user with admin
743 765 rights or regular user that have create repository permission. Regular users
744 766 cannot specify owner parameter
745 767
746 768
747 769 INPUT::
748 770
749 771 id : <id_for_response>
750 772 api_key : "<api_key>"
751 773 method : "create_repo"
752 774 args: {
753 775 "repo_name" : "<reponame>",
754 776 "owner" : "<onwer_name_or_id = Optional(=apiuser)>",
755 777 "repo_type" : "<repo_type> = Optional('hg')",
756 778 "description" : "<description> = Optional('')",
757 779 "private" : "<bool> = Optional(False)",
758 780 "clone_uri" : "<clone_uri> = Optional(None)",
759 781 "landing_rev" : "<landing_rev> = Optional('tip')",
760 782 "enable_downloads": "<bool> = Optional(False)",
761 783 "enable_locking": "<bool> = Optional(False)",
762 784 "enable_statistics": "<bool> = Optional(False)",
763 785 }
764 786
765 787 OUTPUT::
766 788
767 789 id : <id_given_in_input>
768 790 result: {
769 791 "msg": "Created new repository `<reponame>`",
770 792 "repo": {
771 793 "repo_id" : "<repo_id>",
772 794 "repo_name" : "<reponame>"
773 795 "repo_type" : "<repo_type>",
774 796 "clone_uri" : "<clone_uri>",
775 797 "private": : "<bool>",
776 798 "created_on" : "<datetimecreated>",
777 799 "description" : "<description>",
778 800 "landing_rev": "<landing_rev>",
779 801 "owner": "<username or user_id>",
780 802 "fork_of": "<name_of_fork_parent>",
781 803 "enable_downloads": "<bool>",
782 804 "enable_locking": "<bool>",
783 805 "enable_statistics": "<bool>",
784 806 },
785 807 }
786 808 error: null
787 809
788 810
789 811 fork_repo
790 812 ---------
791 813
792 814 Creates a fork of given repo. In case of using celery this will
793 815 immidiatelly return success message, while fork is going to be created
794 816 asynchronous. This command can be executed only using api_key belonging to
795 817 user with admin rights or regular user that have fork permission, and at least
796 818 read access to forking repository. Regular users cannot specify owner parameter.
797 819
798 820
799 821 INPUT::
800 822
801 823 id : <id_for_response>
802 824 api_key : "<api_key>"
803 825 method : "fork_repo"
804 826 args: {
805 827 "repoid" : "<reponame or repo_id>",
806 828 "fork_name": "<forkname>",
807 829 "owner": "<username or user_id = Optional(=apiuser)>",
808 830 "description": "<description>",
809 831 "copy_permissions": "<bool>",
810 832 "private": "<bool>",
811 833 "landing_rev": "<landing_rev>"
812 834
813 835 }
814 836
815 837 OUTPUT::
816 838
817 839 id : <id_given_in_input>
818 840 result: {
819 841 "msg": "Created fork of `<reponame>` as `<forkname>`",
820 842 "success": true
821 843 }
822 844 error: null
823 845
824 846
825 847 delete_repo
826 848 -----------
827 849
828 850 Deletes a repository. This command can be executed only using api_key belonging to user with admin
829 851 rights or regular user that have admin access to repository.
830 852
831 853
832 854 INPUT::
833 855
834 856 id : <id_for_response>
835 857 api_key : "<api_key>"
836 858 method : "delete_repo"
837 859 args: {
838 860 "repoid" : "<reponame or repo_id>"
839 861 }
840 862
841 863 OUTPUT::
842 864
843 865 id : <id_given_in_input>
844 866 result: {
845 867 "msg": "Deleted repository `<reponame>`",
846 868 "success": true
847 869 }
848 870 error: null
849 871
850 872
851 873 grant_user_permission
852 874 ---------------------
853 875
854 876 Grant permission for user on given repository, or update existing one
855 877 if found. This command can be executed only using api_key belonging to user
856 878 with admin rights.
857 879
858 880
859 881 INPUT::
860 882
861 883 id : <id_for_response>
862 884 api_key : "<api_key>"
863 885 method : "grant_user_permission"
864 886 args: {
865 887 "repoid" : "<reponame or repo_id>"
866 888 "userid" : "<username or user_id>"
867 889 "perm" : "(repository.(none|read|write|admin))",
868 890 }
869 891
870 892 OUTPUT::
871 893
872 894 id : <id_given_in_input>
873 895 result: {
874 896 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
875 897 "success": true
876 898 }
877 899 error: null
878 900
879 901
880 902 revoke_user_permission
881 903 ----------------------
882 904
883 905 Revoke permission for user on given repository. This command can be executed
884 906 only using api_key belonging to user with admin rights.
885 907
886 908
887 909 INPUT::
888 910
889 911 id : <id_for_response>
890 912 api_key : "<api_key>"
891 913 method : "revoke_user_permission"
892 914 args: {
893 915 "repoid" : "<reponame or repo_id>"
894 916 "userid" : "<username or user_id>"
895 917 }
896 918
897 919 OUTPUT::
898 920
899 921 id : <id_given_in_input>
900 922 result: {
901 923 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
902 924 "success": true
903 925 }
904 926 error: null
905 927
906 928
907 929 grant_users_group_permission
908 930 ----------------------------
909 931
910 932 Grant permission for users group on given repository, or update
911 933 existing one if found. This command can be executed only using
912 934 api_key belonging to user with admin rights.
913 935
914 936
915 937 INPUT::
916 938
917 939 id : <id_for_response>
918 940 api_key : "<api_key>"
919 941 method : "grant_users_group_permission"
920 942 args: {
921 943 "repoid" : "<reponame or repo_id>"
922 944 "usersgroupid" : "<users group id or name>"
923 945 "perm" : "(repository.(none|read|write|admin))",
924 946 }
925 947
926 948 OUTPUT::
927 949
928 950 id : <id_given_in_input>
929 951 result: {
930 952 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
931 953 "success": true
932 954 }
933 955 error: null
934 956
935 957
936 958 revoke_users_group_permission
937 959 -----------------------------
938 960
939 961 Revoke permission for users group on given repository.This command can be
940 962 executed only using api_key belonging to user with admin rights.
941 963
942 964 INPUT::
943 965
944 966 id : <id_for_response>
945 967 api_key : "<api_key>"
946 968 method : "revoke_users_group_permission"
947 969 args: {
948 970 "repoid" : "<reponame or repo_id>"
949 971 "usersgroupid" : "<users group id or name>"
950 972 }
951 973
952 974 OUTPUT::
953 975
954 976 id : <id_given_in_input>
955 977 result: {
956 978 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
957 979 "success": true
958 980 }
959 981 error: null
@@ -1,932 +1,958 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.api
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 API controller for RhodeCode
7 7
8 8 :created_on: Aug 20, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import traceback
29 29 import logging
30 30 from pylons.controllers.util import abort
31 31
32 32 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
33 33 from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
34 34 HasPermissionAllDecorator, HasPermissionAnyDecorator, \
35 35 HasPermissionAnyApi, HasRepoPermissionAnyApi
36 36 from rhodecode.lib.utils import map_groups, repo2db_mapper
37 37 from rhodecode.model.meta import Session
38 38 from rhodecode.model.scm import ScmModel
39 39 from rhodecode.model.repo import RepoModel
40 40 from rhodecode.model.user import UserModel
41 41 from rhodecode.model.users_group import UsersGroupModel
42 42 from rhodecode.model.permission import PermissionModel
43 43 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class OptionalAttr(object):
49 49 """
50 50 Special Optional Option that defines other attribute
51 51 """
52 52 def __init__(self, attr_name):
53 53 self.attr_name = attr_name
54 54
55 55 def __repr__(self):
56 56 return '<OptionalAttr:%s>' % self.attr_name
57 57
58 58 def __call__(self):
59 59 return self
60 60 #alias
61 61 OAttr = OptionalAttr
62 62
63 63
64 64 class Optional(object):
65 65 """
66 66 Defines an optional parameter::
67 67
68 68 param = param.getval() if isinstance(param, Optional) else param
69 69 param = param() if isinstance(param, Optional) else param
70 70
71 71 is equivalent of::
72 72
73 73 param = Optional.extract(param)
74 74
75 75 """
76 76 def __init__(self, type_):
77 77 self.type_ = type_
78 78
79 79 def __repr__(self):
80 80 return '<Optional:%s>' % self.type_.__repr__()
81 81
82 82 def __call__(self):
83 83 return self.getval()
84 84
85 85 def getval(self):
86 86 """
87 87 returns value from this Optional instance
88 88 """
89 89 return self.type_
90 90
91 91 @classmethod
92 92 def extract(cls, val):
93 93 if isinstance(val, cls):
94 94 return val.getval()
95 95 return val
96 96
97 97
98 98 def get_user_or_error(userid):
99 99 """
100 100 Get user by id or name or return JsonRPCError if not found
101 101
102 102 :param userid:
103 103 """
104 104 user = UserModel().get_user(userid)
105 105 if user is None:
106 106 raise JSONRPCError("user `%s` does not exist" % userid)
107 107 return user
108 108
109 109
110 110 def get_repo_or_error(repoid):
111 111 """
112 112 Get repo by id or name or return JsonRPCError if not found
113 113
114 114 :param userid:
115 115 """
116 116 repo = RepoModel().get_repo(repoid)
117 117 if repo is None:
118 118 raise JSONRPCError('repository `%s` does not exist' % (repoid))
119 119 return repo
120 120
121 121
122 122 def get_users_group_or_error(usersgroupid):
123 123 """
124 124 Get users group by id or name or return JsonRPCError if not found
125 125
126 126 :param userid:
127 127 """
128 128 users_group = UsersGroupModel().get_group(usersgroupid)
129 129 if users_group is None:
130 130 raise JSONRPCError('users group `%s` does not exist' % usersgroupid)
131 131 return users_group
132 132
133 133
134 134 def get_perm_or_error(permid):
135 135 """
136 136 Get permission by id or name or return JsonRPCError if not found
137 137
138 138 :param userid:
139 139 """
140 140 perm = PermissionModel().get_permission_by_name(permid)
141 141 if perm is None:
142 142 raise JSONRPCError('permission `%s` does not exist' % (permid))
143 143 return perm
144 144
145 145
146 146 class ApiController(JSONRPCController):
147 147 """
148 148 API Controller
149 149
150 150
151 151 Each method needs to have USER as argument this is then based on given
152 152 API_KEY propagated as instance of user object
153 153
154 154 Preferably this should be first argument also
155 155
156 156
157 157 Each function should also **raise** JSONRPCError for any
158 158 errors that happens
159 159
160 160 """
161 161
162 162 @HasPermissionAllDecorator('hg.admin')
163 163 def pull(self, apiuser, repoid):
164 164 """
165 165 Dispatch pull action on given repo
166 166
167 167 :param apiuser:
168 168 :param repoid:
169 169 """
170 170
171 171 repo = get_repo_or_error(repoid)
172 172
173 173 try:
174 174 ScmModel().pull_changes(repo.repo_name,
175 175 self.rhodecode_user.username)
176 176 return 'Pulled from `%s`' % repo.repo_name
177 177 except Exception:
178 178 log.error(traceback.format_exc())
179 179 raise JSONRPCError(
180 180 'Unable to pull changes from `%s`' % repo.repo_name
181 181 )
182 182
183 183 @HasPermissionAllDecorator('hg.admin')
184 184 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
185 185 """
186 186 Dispatch rescan repositories action. If remove_obsolete is set
187 187 than also delete repos that are in database but not in the filesystem.
188 188 aka "clean zombies"
189 189
190 190 :param apiuser:
191 191 :param remove_obsolete:
192 192 """
193 193
194 194 try:
195 195 rm_obsolete = Optional.extract(remove_obsolete)
196 196 added, removed = repo2db_mapper(ScmModel().repo_scan(),
197 197 remove_obsolete=rm_obsolete)
198 198 return {'added': added, 'removed': removed}
199 199 except Exception:
200 200 log.error(traceback.format_exc())
201 201 raise JSONRPCError(
202 202 'Error occurred during rescan repositories action'
203 203 )
204 204
205 def invalidate_cache(self, apiuser, repoid):
206 """
207 Dispatch cache invalidation action on given repo
208
209 :param apiuser:
210 :param repoid:
211 """
212 repo = get_repo_or_error(repoid)
213 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
214 # check if we have admin permission for this repo !
215 if HasRepoPermissionAnyApi('repository.admin',
216 'repository.write')(user=apiuser,
217 repo_name=repo.repo_name) is False:
218 raise JSONRPCError('repository `%s` does not exist' % (repoid))
219
220 try:
221 invalidated_keys = ScmModel().mark_for_invalidation(repo.repo_name)
222 Session().commit()
223 return ('Cache for repository `%s` was invalidated: '
224 'invalidated cache keys: %s' % (repoid, invalidated_keys))
225 except Exception:
226 log.error(traceback.format_exc())
227 raise JSONRPCError(
228 'Error occurred during cache invalidation action'
229 )
230
205 231 def lock(self, apiuser, repoid, locked, userid=Optional(OAttr('apiuser'))):
206 232 """
207 233 Set locking state on particular repository by given user, if
208 234 this command is runned by non-admin account userid is set to user
209 235 who is calling this method
210 236
211 237 :param apiuser:
212 238 :param repoid:
213 239 :param userid:
214 240 :param locked:
215 241 """
216 242 repo = get_repo_or_error(repoid)
217 243 if HasPermissionAnyApi('hg.admin')(user=apiuser):
218 244 pass
219 245 elif HasRepoPermissionAnyApi('repository.admin',
220 246 'repository.write')(user=apiuser,
221 247 repo_name=repo.repo_name):
222 248 #make sure normal user does not pass someone else userid,
223 249 #he is not allowed to do that
224 250 if not isinstance(userid, Optional) and userid != apiuser.user_id:
225 251 raise JSONRPCError(
226 252 'userid is not the same as your user'
227 253 )
228 254 else:
229 255 raise JSONRPCError('repository `%s` does not exist' % (repoid))
230 256
231 257 if isinstance(userid, Optional):
232 258 userid = apiuser.user_id
233 259 user = get_user_or_error(userid)
234 260 locked = bool(locked)
235 261 try:
236 262 if locked:
237 263 Repository.lock(repo, user.user_id)
238 264 else:
239 265 Repository.unlock(repo)
240 266
241 267 return ('User `%s` set lock state for repo `%s` to `%s`'
242 268 % (user.username, repo.repo_name, locked))
243 269 except Exception:
244 270 log.error(traceback.format_exc())
245 271 raise JSONRPCError(
246 272 'Error occurred locking repository `%s`' % repo.repo_name
247 273 )
248 274
249 275 @HasPermissionAllDecorator('hg.admin')
250 276 def show_ip(self, apiuser, userid):
251 277 """
252 278 Shows IP address as seen from RhodeCode server, together with all
253 279 defined IP addresses for given user
254 280
255 281 :param apiuser:
256 282 :param userid:
257 283 """
258 284 user = get_user_or_error(userid)
259 285 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
260 286 return dict(
261 287 ip_addr_server=self.ip_addr,
262 288 user_ips=ips
263 289 )
264 290
265 291 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
266 292 """"
267 293 Get a user by username, or userid, if userid is given
268 294
269 295 :param apiuser:
270 296 :param userid:
271 297 """
272 298 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
273 299 #make sure normal user does not pass someone else userid,
274 300 #he is not allowed to do that
275 301 if not isinstance(userid, Optional) and userid != apiuser.user_id:
276 302 raise JSONRPCError(
277 303 'userid is not the same as your user'
278 304 )
279 305
280 306 if isinstance(userid, Optional):
281 307 userid = apiuser.user_id
282 308
283 309 user = get_user_or_error(userid)
284 310 data = user.get_api_data()
285 311 data['permissions'] = AuthUser(user_id=user.user_id).permissions
286 312 return data
287 313
288 314 @HasPermissionAllDecorator('hg.admin')
289 315 def get_users(self, apiuser):
290 316 """"
291 317 Get all users
292 318
293 319 :param apiuser:
294 320 """
295 321
296 322 result = []
297 323 for user in UserModel().get_all():
298 324 result.append(user.get_api_data())
299 325 return result
300 326
301 327 @HasPermissionAllDecorator('hg.admin')
302 328 def create_user(self, apiuser, username, email, password,
303 329 firstname=Optional(None), lastname=Optional(None),
304 330 active=Optional(True), admin=Optional(False),
305 331 ldap_dn=Optional(None)):
306 332 """
307 333 Create new user
308 334
309 335 :param apiuser:
310 336 :param username:
311 337 :param email:
312 338 :param password:
313 339 :param firstname:
314 340 :param lastname:
315 341 :param active:
316 342 :param admin:
317 343 :param ldap_dn:
318 344 """
319 345
320 346 if UserModel().get_by_username(username):
321 347 raise JSONRPCError("user `%s` already exist" % username)
322 348
323 349 if UserModel().get_by_email(email, case_insensitive=True):
324 350 raise JSONRPCError("email `%s` already exist" % email)
325 351
326 352 if Optional.extract(ldap_dn):
327 353 # generate temporary password if ldap_dn
328 354 password = PasswordGenerator().gen_password(length=8)
329 355
330 356 try:
331 357 user = UserModel().create_or_update(
332 358 username=Optional.extract(username),
333 359 password=Optional.extract(password),
334 360 email=Optional.extract(email),
335 361 firstname=Optional.extract(firstname),
336 362 lastname=Optional.extract(lastname),
337 363 active=Optional.extract(active),
338 364 admin=Optional.extract(admin),
339 365 ldap_dn=Optional.extract(ldap_dn)
340 366 )
341 367 Session().commit()
342 368 return dict(
343 369 msg='created new user `%s`' % username,
344 370 user=user.get_api_data()
345 371 )
346 372 except Exception:
347 373 log.error(traceback.format_exc())
348 374 raise JSONRPCError('failed to create user `%s`' % username)
349 375
350 376 @HasPermissionAllDecorator('hg.admin')
351 377 def update_user(self, apiuser, userid, username=Optional(None),
352 378 email=Optional(None), firstname=Optional(None),
353 379 lastname=Optional(None), active=Optional(None),
354 380 admin=Optional(None), ldap_dn=Optional(None),
355 381 password=Optional(None)):
356 382 """
357 383 Updates given user
358 384
359 385 :param apiuser:
360 386 :param userid:
361 387 :param username:
362 388 :param email:
363 389 :param firstname:
364 390 :param lastname:
365 391 :param active:
366 392 :param admin:
367 393 :param ldap_dn:
368 394 :param password:
369 395 """
370 396
371 397 user = get_user_or_error(userid)
372 398
373 399 # call function and store only updated arguments
374 400 updates = {}
375 401
376 402 def store_update(attr, name):
377 403 if not isinstance(attr, Optional):
378 404 updates[name] = attr
379 405
380 406 try:
381 407
382 408 store_update(username, 'username')
383 409 store_update(password, 'password')
384 410 store_update(email, 'email')
385 411 store_update(firstname, 'name')
386 412 store_update(lastname, 'lastname')
387 413 store_update(active, 'active')
388 414 store_update(admin, 'admin')
389 415 store_update(ldap_dn, 'ldap_dn')
390 416
391 417 user = UserModel().update_user(user, **updates)
392 418 Session().commit()
393 419 return dict(
394 420 msg='updated user ID:%s %s' % (user.user_id, user.username),
395 421 user=user.get_api_data()
396 422 )
397 423 except Exception:
398 424 log.error(traceback.format_exc())
399 425 raise JSONRPCError('failed to update user `%s`' % userid)
400 426
401 427 @HasPermissionAllDecorator('hg.admin')
402 428 def delete_user(self, apiuser, userid):
403 429 """"
404 430 Deletes an user
405 431
406 432 :param apiuser:
407 433 :param userid:
408 434 """
409 435 user = get_user_or_error(userid)
410 436
411 437 try:
412 438 UserModel().delete(userid)
413 439 Session().commit()
414 440 return dict(
415 441 msg='deleted user ID:%s %s' % (user.user_id, user.username),
416 442 user=None
417 443 )
418 444 except Exception:
419 445 log.error(traceback.format_exc())
420 446 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
421 447 user.username))
422 448
423 449 @HasPermissionAllDecorator('hg.admin')
424 450 def get_users_group(self, apiuser, usersgroupid):
425 451 """"
426 452 Get users group by name or id
427 453
428 454 :param apiuser:
429 455 :param usersgroupid:
430 456 """
431 457 users_group = get_users_group_or_error(usersgroupid)
432 458
433 459 data = users_group.get_api_data()
434 460
435 461 members = []
436 462 for user in users_group.members:
437 463 user = user.user
438 464 members.append(user.get_api_data())
439 465 data['members'] = members
440 466 return data
441 467
442 468 @HasPermissionAllDecorator('hg.admin')
443 469 def get_users_groups(self, apiuser):
444 470 """"
445 471 Get all users groups
446 472
447 473 :param apiuser:
448 474 """
449 475
450 476 result = []
451 477 for users_group in UsersGroupModel().get_all():
452 478 result.append(users_group.get_api_data())
453 479 return result
454 480
455 481 @HasPermissionAllDecorator('hg.admin')
456 482 def create_users_group(self, apiuser, group_name, active=Optional(True)):
457 483 """
458 484 Creates an new usergroup
459 485
460 486 :param apiuser:
461 487 :param group_name:
462 488 :param active:
463 489 """
464 490
465 491 if UsersGroupModel().get_by_name(group_name):
466 492 raise JSONRPCError("users group `%s` already exist" % group_name)
467 493
468 494 try:
469 495 active = Optional.extract(active)
470 496 ug = UsersGroupModel().create(name=group_name, active=active)
471 497 Session().commit()
472 498 return dict(
473 499 msg='created new users group `%s`' % group_name,
474 500 users_group=ug.get_api_data()
475 501 )
476 502 except Exception:
477 503 log.error(traceback.format_exc())
478 504 raise JSONRPCError('failed to create group `%s`' % group_name)
479 505
480 506 @HasPermissionAllDecorator('hg.admin')
481 507 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
482 508 """"
483 509 Add a user to a users group
484 510
485 511 :param apiuser:
486 512 :param usersgroupid:
487 513 :param userid:
488 514 """
489 515 user = get_user_or_error(userid)
490 516 users_group = get_users_group_or_error(usersgroupid)
491 517
492 518 try:
493 519 ugm = UsersGroupModel().add_user_to_group(users_group, user)
494 520 success = True if ugm != True else False
495 521 msg = 'added member `%s` to users group `%s`' % (
496 522 user.username, users_group.users_group_name
497 523 )
498 524 msg = msg if success else 'User is already in that group'
499 525 Session().commit()
500 526
501 527 return dict(
502 528 success=success,
503 529 msg=msg
504 530 )
505 531 except Exception:
506 532 log.error(traceback.format_exc())
507 533 raise JSONRPCError(
508 534 'failed to add member to users group `%s`' % (
509 535 users_group.users_group_name
510 536 )
511 537 )
512 538
513 539 @HasPermissionAllDecorator('hg.admin')
514 540 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
515 541 """
516 542 Remove user from a group
517 543
518 544 :param apiuser:
519 545 :param usersgroupid:
520 546 :param userid:
521 547 """
522 548 user = get_user_or_error(userid)
523 549 users_group = get_users_group_or_error(usersgroupid)
524 550
525 551 try:
526 552 success = UsersGroupModel().remove_user_from_group(users_group,
527 553 user)
528 554 msg = 'removed member `%s` from users group `%s`' % (
529 555 user.username, users_group.users_group_name
530 556 )
531 557 msg = msg if success else "User wasn't in group"
532 558 Session().commit()
533 559 return dict(success=success, msg=msg)
534 560 except Exception:
535 561 log.error(traceback.format_exc())
536 562 raise JSONRPCError(
537 563 'failed to remove member from users group `%s`' % (
538 564 users_group.users_group_name
539 565 )
540 566 )
541 567
542 568 def get_repo(self, apiuser, repoid):
543 569 """"
544 570 Get repository by name
545 571
546 572 :param apiuser:
547 573 :param repoid:
548 574 """
549 575 repo = get_repo_or_error(repoid)
550 576
551 577 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
552 578 # check if we have admin permission for this repo !
553 579 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
554 580 repo_name=repo.repo_name) is False:
555 581 raise JSONRPCError('repository `%s` does not exist' % (repoid))
556 582
557 583 members = []
558 584 followers = []
559 585 for user in repo.repo_to_perm:
560 586 perm = user.permission.permission_name
561 587 user = user.user
562 588 user_data = user.get_api_data()
563 589 user_data['type'] = "user"
564 590 user_data['permission'] = perm
565 591 members.append(user_data)
566 592
567 593 for users_group in repo.users_group_to_perm:
568 594 perm = users_group.permission.permission_name
569 595 users_group = users_group.users_group
570 596 users_group_data = users_group.get_api_data()
571 597 users_group_data['type'] = "users_group"
572 598 users_group_data['permission'] = perm
573 599 members.append(users_group_data)
574 600
575 601 for user in repo.followers:
576 602 followers.append(user.user.get_api_data())
577 603
578 604 data = repo.get_api_data()
579 605 data['members'] = members
580 606 data['followers'] = followers
581 607 return data
582 608
583 609 def get_repos(self, apiuser):
584 610 """"
585 611 Get all repositories
586 612
587 613 :param apiuser:
588 614 """
589 615 result = []
590 616 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
591 617 repos = RepoModel().get_all_user_repos(user=apiuser)
592 618 else:
593 619 repos = RepoModel().get_all()
594 620
595 621 for repo in repos:
596 622 result.append(repo.get_api_data())
597 623 return result
598 624
599 625 @HasPermissionAllDecorator('hg.admin')
600 626 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
601 627 ret_type='all'):
602 628 """
603 629 returns a list of nodes and it's children
604 630 for a given path at given revision. It's possible to specify ret_type
605 631 to show only files or dirs
606 632
607 633 :param apiuser:
608 634 :param repoid: name or id of repository
609 635 :param revision: revision for which listing should be done
610 636 :param root_path: path from which start displaying
611 637 :param ret_type: return type 'all|files|dirs' nodes
612 638 """
613 639 repo = get_repo_or_error(repoid)
614 640 try:
615 641 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
616 642 flat=False)
617 643 _map = {
618 644 'all': _d + _f,
619 645 'files': _f,
620 646 'dirs': _d,
621 647 }
622 648 return _map[ret_type]
623 649 except KeyError:
624 650 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
625 651 except Exception:
626 652 log.error(traceback.format_exc())
627 653 raise JSONRPCError(
628 654 'failed to get repo: `%s` nodes' % repo.repo_name
629 655 )
630 656
631 657 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
632 658 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
633 659 repo_type=Optional('hg'),
634 660 description=Optional(''), private=Optional(False),
635 661 clone_uri=Optional(None), landing_rev=Optional('tip'),
636 662 enable_statistics=Optional(False),
637 663 enable_locking=Optional(False),
638 664 enable_downloads=Optional(False)):
639 665 """
640 666 Create repository, if clone_url is given it makes a remote clone
641 667 if repo_name is within a group name the groups will be created
642 668 automatically if they aren't present
643 669
644 670 :param apiuser:
645 671 :param repo_name:
646 672 :param onwer:
647 673 :param repo_type:
648 674 :param description:
649 675 :param private:
650 676 :param clone_uri:
651 677 :param landing_rev:
652 678 """
653 679 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
654 680 if not isinstance(owner, Optional):
655 681 #forbid setting owner for non-admins
656 682 raise JSONRPCError(
657 683 'Only RhodeCode admin can specify `owner` param'
658 684 )
659 685 if isinstance(owner, Optional):
660 686 owner = apiuser.user_id
661 687
662 688 owner = get_user_or_error(owner)
663 689
664 690 if RepoModel().get_by_repo_name(repo_name):
665 691 raise JSONRPCError("repo `%s` already exist" % repo_name)
666 692
667 693 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
668 694 if isinstance(private, Optional):
669 695 private = defs.get('repo_private') or Optional.extract(private)
670 696 if isinstance(repo_type, Optional):
671 697 repo_type = defs.get('repo_type')
672 698 if isinstance(enable_statistics, Optional):
673 699 enable_statistics = defs.get('repo_enable_statistics')
674 700 if isinstance(enable_locking, Optional):
675 701 enable_locking = defs.get('repo_enable_locking')
676 702 if isinstance(enable_downloads, Optional):
677 703 enable_downloads = defs.get('repo_enable_downloads')
678 704
679 705 clone_uri = Optional.extract(clone_uri)
680 706 description = Optional.extract(description)
681 707 landing_rev = Optional.extract(landing_rev)
682 708
683 709 try:
684 710 # create structure of groups and return the last group
685 711 group = map_groups(repo_name)
686 712
687 713 repo = RepoModel().create_repo(
688 714 repo_name=repo_name,
689 715 repo_type=repo_type,
690 716 description=description,
691 717 owner=owner,
692 718 private=private,
693 719 clone_uri=clone_uri,
694 720 repos_group=group,
695 721 landing_rev=landing_rev,
696 722 enable_statistics=enable_statistics,
697 723 enable_downloads=enable_downloads,
698 724 enable_locking=enable_locking
699 725 )
700 726
701 727 Session().commit()
702 728 return dict(
703 729 msg="Created new repository `%s`" % (repo.repo_name),
704 730 repo=repo.get_api_data()
705 731 )
706 732 except Exception:
707 733 log.error(traceback.format_exc())
708 734 raise JSONRPCError('failed to create repository `%s`' % repo_name)
709 735
710 736 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
711 737 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
712 738 description=Optional(''), copy_permissions=Optional(False),
713 739 private=Optional(False), landing_rev=Optional('tip')):
714 740 repo = get_repo_or_error(repoid)
715 741 repo_name = repo.repo_name
716 742
717 743 _repo = RepoModel().get_by_repo_name(fork_name)
718 744 if _repo:
719 745 type_ = 'fork' if _repo.fork else 'repo'
720 746 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
721 747
722 748 if HasPermissionAnyApi('hg.admin')(user=apiuser):
723 749 pass
724 750 elif HasRepoPermissionAnyApi('repository.admin',
725 751 'repository.write',
726 752 'repository.read')(user=apiuser,
727 753 repo_name=repo.repo_name):
728 754 if not isinstance(owner, Optional):
729 755 #forbid setting owner for non-admins
730 756 raise JSONRPCError(
731 757 'Only RhodeCode admin can specify `owner` param'
732 758 )
733 759 else:
734 760 raise JSONRPCError('repository `%s` does not exist' % (repoid))
735 761
736 762 if isinstance(owner, Optional):
737 763 owner = apiuser.user_id
738 764
739 765 owner = get_user_or_error(owner)
740 766
741 767 try:
742 768 # create structure of groups and return the last group
743 769 group = map_groups(fork_name)
744 770
745 771 form_data = dict(
746 772 repo_name=fork_name,
747 773 repo_name_full=fork_name,
748 774 repo_group=group,
749 775 repo_type=repo.repo_type,
750 776 description=Optional.extract(description),
751 777 private=Optional.extract(private),
752 778 copy_permissions=Optional.extract(copy_permissions),
753 779 landing_rev=Optional.extract(landing_rev),
754 780 update_after_clone=False,
755 781 fork_parent_id=repo.repo_id,
756 782 )
757 783 RepoModel().create_fork(form_data, cur_user=owner)
758 784 return dict(
759 785 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
760 786 fork_name),
761 787 success=True # cannot return the repo data here since fork
762 788 # cann be done async
763 789 )
764 790 except Exception:
765 791 log.error(traceback.format_exc())
766 792 raise JSONRPCError(
767 793 'failed to fork repository `%s` as `%s`' % (repo_name,
768 794 fork_name)
769 795 )
770 796
771 797 def delete_repo(self, apiuser, repoid):
772 798 """
773 799 Deletes a given repository
774 800
775 801 :param apiuser:
776 802 :param repoid:
777 803 """
778 804 repo = get_repo_or_error(repoid)
779 805
780 806 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
781 807 # check if we have admin permission for this repo !
782 808 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
783 809 repo_name=repo.repo_name) is False:
784 810 raise JSONRPCError('repository `%s` does not exist' % (repoid))
785 811
786 812 try:
787 813 RepoModel().delete(repo)
788 814 Session().commit()
789 815 return dict(
790 816 msg='Deleted repository `%s`' % repo.repo_name,
791 817 success=True
792 818 )
793 819 except Exception:
794 820 log.error(traceback.format_exc())
795 821 raise JSONRPCError(
796 822 'failed to delete repository `%s`' % repo.repo_name
797 823 )
798 824
799 825 @HasPermissionAllDecorator('hg.admin')
800 826 def grant_user_permission(self, apiuser, repoid, userid, perm):
801 827 """
802 828 Grant permission for user on given repository, or update existing one
803 829 if found
804 830
805 831 :param repoid:
806 832 :param userid:
807 833 :param perm:
808 834 """
809 835 repo = get_repo_or_error(repoid)
810 836 user = get_user_or_error(userid)
811 837 perm = get_perm_or_error(perm)
812 838
813 839 try:
814 840
815 841 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
816 842
817 843 Session().commit()
818 844 return dict(
819 845 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
820 846 perm.permission_name, user.username, repo.repo_name
821 847 ),
822 848 success=True
823 849 )
824 850 except Exception:
825 851 log.error(traceback.format_exc())
826 852 raise JSONRPCError(
827 853 'failed to edit permission for user: `%s` in repo: `%s`' % (
828 854 userid, repoid
829 855 )
830 856 )
831 857
832 858 @HasPermissionAllDecorator('hg.admin')
833 859 def revoke_user_permission(self, apiuser, repoid, userid):
834 860 """
835 861 Revoke permission for user on given repository
836 862
837 863 :param apiuser:
838 864 :param repoid:
839 865 :param userid:
840 866 """
841 867
842 868 repo = get_repo_or_error(repoid)
843 869 user = get_user_or_error(userid)
844 870 try:
845 871
846 872 RepoModel().revoke_user_permission(repo=repo, user=user)
847 873
848 874 Session().commit()
849 875 return dict(
850 876 msg='Revoked perm for user: `%s` in repo: `%s`' % (
851 877 user.username, repo.repo_name
852 878 ),
853 879 success=True
854 880 )
855 881 except Exception:
856 882 log.error(traceback.format_exc())
857 883 raise JSONRPCError(
858 884 'failed to edit permission for user: `%s` in repo: `%s`' % (
859 885 userid, repoid
860 886 )
861 887 )
862 888
863 889 @HasPermissionAllDecorator('hg.admin')
864 890 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
865 891 perm):
866 892 """
867 893 Grant permission for users group on given repository, or update
868 894 existing one if found
869 895
870 896 :param apiuser:
871 897 :param repoid:
872 898 :param usersgroupid:
873 899 :param perm:
874 900 """
875 901 repo = get_repo_or_error(repoid)
876 902 perm = get_perm_or_error(perm)
877 903 users_group = get_users_group_or_error(usersgroupid)
878 904
879 905 try:
880 906 RepoModel().grant_users_group_permission(repo=repo,
881 907 group_name=users_group,
882 908 perm=perm)
883 909
884 910 Session().commit()
885 911 return dict(
886 912 msg='Granted perm: `%s` for users group: `%s` in '
887 913 'repo: `%s`' % (
888 914 perm.permission_name, users_group.users_group_name,
889 915 repo.repo_name
890 916 ),
891 917 success=True
892 918 )
893 919 except Exception:
894 920 log.error(traceback.format_exc())
895 921 raise JSONRPCError(
896 922 'failed to edit permission for users group: `%s` in '
897 923 'repo: `%s`' % (
898 924 usersgroupid, repo.repo_name
899 925 )
900 926 )
901 927
902 928 @HasPermissionAllDecorator('hg.admin')
903 929 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
904 930 """
905 931 Revoke permission for users group on given repository
906 932
907 933 :param apiuser:
908 934 :param repoid:
909 935 :param usersgroupid:
910 936 """
911 937 repo = get_repo_or_error(repoid)
912 938 users_group = get_users_group_or_error(usersgroupid)
913 939
914 940 try:
915 941 RepoModel().revoke_users_group_permission(repo=repo,
916 942 group_name=users_group)
917 943
918 944 Session().commit()
919 945 return dict(
920 946 msg='Revoked perm for users group: `%s` in repo: `%s`' % (
921 947 users_group.users_group_name, repo.repo_name
922 948 ),
923 949 success=True
924 950 )
925 951 except Exception:
926 952 log.error(traceback.format_exc())
927 953 raise JSONRPCError(
928 954 'failed to edit permission for users group: `%s` in '
929 955 'repo: `%s`' % (
930 956 users_group.users_group_name, repo.repo_name
931 957 )
932 958 )
@@ -1,1275 +1,1294 b''
1 1 from __future__ import with_statement
2 2 import random
3 3 import mock
4 4
5 5 from rhodecode.tests import *
6 6 from rhodecode.lib.compat import json
7 7 from rhodecode.lib.auth import AuthUser
8 8 from rhodecode.model.user import UserModel
9 9 from rhodecode.model.users_group import UsersGroupModel
10 10 from rhodecode.model.repo import RepoModel
11 11 from rhodecode.model.meta import Session
12 12 from rhodecode.model.scm import ScmModel
13 13 from rhodecode.model.db import Repository
14 14
15 15 API_URL = '/_admin/api'
16 16
17 17
18 18 def _build_data(apikey, method, **kw):
19 19 """
20 20 Builds API data with given random ID
21 21
22 22 :param random_id:
23 23 :type random_id:
24 24 """
25 25 random_id = random.randrange(1, 9999)
26 26 return random_id, json.dumps({
27 27 "id": random_id,
28 28 "api_key": apikey,
29 29 "method": method,
30 30 "args": kw
31 31 })
32 32
33 33 jsonify = lambda obj: json.loads(json.dumps(obj))
34 34
35 35
36 36 def crash(*args, **kwargs):
37 37 raise Exception('Total Crash !')
38 38
39 39
40 40 def api_call(test_obj, params):
41 41 response = test_obj.app.post(API_URL, content_type='application/json',
42 42 params=params)
43 43 return response
44 44
45 45
46 46 TEST_USERS_GROUP = 'test_users_group'
47 47
48 48
49 49 def make_users_group(name=TEST_USERS_GROUP):
50 50 gr = UsersGroupModel().create(name=name)
51 51 UsersGroupModel().add_user_to_group(users_group=gr,
52 52 user=TEST_USER_ADMIN_LOGIN)
53 53 Session().commit()
54 54 return gr
55 55
56 56
57 57 def destroy_users_group(name=TEST_USERS_GROUP):
58 58 UsersGroupModel().delete(users_group=name, force=True)
59 59 Session().commit()
60 60
61 61
62 62 def create_repo(repo_name, repo_type, owner=None):
63 63 # create new repo
64 64 form_data = _get_repo_create_params(
65 65 repo_name_full=repo_name,
66 66 repo_description='description %s' % repo_name,
67 67 )
68 68 cur_user = UserModel().get_by_username(owner or TEST_USER_ADMIN_LOGIN)
69 69 r = RepoModel().create(form_data, cur_user)
70 70 Session().commit()
71 71 return r
72 72
73 73
74 74 def create_fork(fork_name, fork_type, fork_of):
75 75 fork = RepoModel(Session())._get_repo(fork_of)
76 76 r = create_repo(fork_name, fork_type)
77 77 r.fork = fork
78 78 Session().add(r)
79 79 Session().commit()
80 80 return r
81 81
82 82
83 83 def destroy_repo(repo_name):
84 84 RepoModel().delete(repo_name)
85 85 Session().commit()
86 86
87 87
88 88 class BaseTestApi(object):
89 89 REPO = None
90 90 REPO_TYPE = None
91 91
92 92 @classmethod
93 93 def setUpClass(self):
94 94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
95 95 self.apikey = self.usr.api_key
96 96 self.test_user = UserModel().create_or_update(
97 97 username='test-api',
98 98 password='test',
99 99 email='test@api.rhodecode.org',
100 100 firstname='first',
101 101 lastname='last'
102 102 )
103 103 Session().commit()
104 104 self.TEST_USER_LOGIN = self.test_user.username
105 105 self.apikey_regular = self.test_user.api_key
106 106
107 107 @classmethod
108 108 def teardownClass(self):
109 109 pass
110 110
111 111 def setUp(self):
112 112 self.maxDiff = None
113 113 make_users_group()
114 114
115 115 def tearDown(self):
116 116 destroy_users_group()
117 117
118 118 def _compare_ok(self, id_, expected, given):
119 119 expected = jsonify({
120 120 'id': id_,
121 121 'error': None,
122 122 'result': expected
123 123 })
124 124 given = json.loads(given)
125 125 self.assertEqual(expected, given)
126 126
127 127 def _compare_error(self, id_, expected, given):
128 128 expected = jsonify({
129 129 'id': id_,
130 130 'error': expected,
131 131 'result': None
132 132 })
133 133 given = json.loads(given)
134 134 self.assertEqual(expected, given)
135 135
136 136 # def test_Optional(self):
137 137 # from rhodecode.controllers.api.api import Optional
138 138 # option1 = Optional(None)
139 139 # self.assertEqual('<Optional:%s>' % None, repr(option1))
140 140 #
141 141 # self.assertEqual(1, Optional.extract(Optional(1)))
142 142 # self.assertEqual('trololo', Optional.extract('trololo'))
143 143
144 144 def test_api_wrong_key(self):
145 145 id_, params = _build_data('trololo', 'get_user')
146 146 response = api_call(self, params)
147 147
148 148 expected = 'Invalid API KEY'
149 149 self._compare_error(id_, expected, given=response.body)
150 150
151 151 def test_api_missing_non_optional_param(self):
152 152 id_, params = _build_data(self.apikey, 'get_repo')
153 153 response = api_call(self, params)
154 154
155 155 expected = 'Missing non optional `repoid` arg in JSON DATA'
156 156 self._compare_error(id_, expected, given=response.body)
157 157
158 158 def test_api_missing_non_optional_param_args_null(self):
159 159 id_, params = _build_data(self.apikey, 'get_repo')
160 160 params = params.replace('"args": {}', '"args": null')
161 161 response = api_call(self, params)
162 162
163 163 expected = 'Missing non optional `repoid` arg in JSON DATA'
164 164 self._compare_error(id_, expected, given=response.body)
165 165
166 166 def test_api_missing_non_optional_param_args_bad(self):
167 167 id_, params = _build_data(self.apikey, 'get_repo')
168 168 params = params.replace('"args": {}', '"args": 1')
169 169 response = api_call(self, params)
170 170
171 171 expected = 'Missing non optional `repoid` arg in JSON DATA'
172 172 self._compare_error(id_, expected, given=response.body)
173 173
174 174 def test_api_args_is_null(self):
175 175 id_, params = _build_data(self.apikey, 'get_users',)
176 176 params = params.replace('"args": {}', '"args": null')
177 177 response = api_call(self, params)
178 178 self.assertEqual(response.status, '200 OK')
179 179
180 180 def test_api_args_is_bad(self):
181 181 id_, params = _build_data(self.apikey, 'get_users',)
182 182 params = params.replace('"args": {}', '"args": 1')
183 183 response = api_call(self, params)
184 184 self.assertEqual(response.status, '200 OK')
185 185
186 186 def test_api_get_users(self):
187 187 id_, params = _build_data(self.apikey, 'get_users',)
188 188 response = api_call(self, params)
189 189 ret_all = []
190 190 for usr in UserModel().get_all():
191 191 ret = usr.get_api_data()
192 192 ret_all.append(jsonify(ret))
193 193 expected = ret_all
194 194 self._compare_ok(id_, expected, given=response.body)
195 195
196 196 def test_api_get_user(self):
197 197 id_, params = _build_data(self.apikey, 'get_user',
198 198 userid=TEST_USER_ADMIN_LOGIN)
199 199 response = api_call(self, params)
200 200
201 201 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
202 202 ret = usr.get_api_data()
203 203 ret['permissions'] = AuthUser(usr.user_id).permissions
204 204
205 205 expected = ret
206 206 self._compare_ok(id_, expected, given=response.body)
207 207
208 208 def test_api_get_user_that_does_not_exist(self):
209 209 id_, params = _build_data(self.apikey, 'get_user',
210 210 userid='trololo')
211 211 response = api_call(self, params)
212 212
213 213 expected = "user `%s` does not exist" % 'trololo'
214 214 self._compare_error(id_, expected, given=response.body)
215 215
216 216 def test_api_get_user_without_giving_userid(self):
217 217 id_, params = _build_data(self.apikey, 'get_user')
218 218 response = api_call(self, params)
219 219
220 220 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
221 221 ret = usr.get_api_data()
222 222 ret['permissions'] = AuthUser(usr.user_id).permissions
223 223
224 224 expected = ret
225 225 self._compare_ok(id_, expected, given=response.body)
226 226
227 227 def test_api_get_user_without_giving_userid_non_admin(self):
228 228 id_, params = _build_data(self.apikey_regular, 'get_user')
229 229 response = api_call(self, params)
230 230
231 231 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
232 232 ret = usr.get_api_data()
233 233 ret['permissions'] = AuthUser(usr.user_id).permissions
234 234
235 235 expected = ret
236 236 self._compare_ok(id_, expected, given=response.body)
237 237
238 238 def test_api_get_user_with_giving_userid_non_admin(self):
239 239 id_, params = _build_data(self.apikey_regular, 'get_user',
240 240 userid=self.TEST_USER_LOGIN)
241 241 response = api_call(self, params)
242 242
243 243 expected = 'userid is not the same as your user'
244 244 self._compare_error(id_, expected, given=response.body)
245 245
246 246 def test_api_pull(self):
247 247 #TODO: issues with rhodecode_extras here.. not sure why !
248 248 pass
249 249
250 250 # repo_name = 'test_pull'
251 251 # r = create_repo(repo_name, self.REPO_TYPE)
252 252 # r.clone_uri = TEST_self.REPO
253 253 # Session.add(r)
254 254 # Session.commit()
255 255 #
256 256 # id_, params = _build_data(self.apikey, 'pull',
257 257 # repoid=repo_name,)
258 258 # response = self.app.post(API_URL, content_type='application/json',
259 259 # params=params)
260 260 #
261 261 # expected = 'Pulled from `%s`' % repo_name
262 262 # self._compare_ok(id_, expected, given=response.body)
263 263 #
264 264 # destroy_repo(repo_name)
265 265
266 266 def test_api_pull_error(self):
267 267 id_, params = _build_data(self.apikey, 'pull',
268 268 repoid=self.REPO,)
269 269 response = api_call(self, params)
270 270
271 271 expected = 'Unable to pull changes from `%s`' % self.REPO
272 272 self._compare_error(id_, expected, given=response.body)
273 273
274 274 def test_api_rescan_repos(self):
275 275 id_, params = _build_data(self.apikey, 'rescan_repos')
276 276 response = api_call(self, params)
277 277
278 278 expected = {'added': [], 'removed': []}
279 279 self._compare_ok(id_, expected, given=response.body)
280 280
281 281 @mock.patch.object(ScmModel, 'repo_scan', crash)
282 282 def test_api_rescann_error(self):
283 283 id_, params = _build_data(self.apikey, 'rescan_repos',)
284 284 response = api_call(self, params)
285 285
286 286 expected = 'Error occurred during rescan repositories action'
287 287 self._compare_error(id_, expected, given=response.body)
288 288
289 def test_api_invalidate_cache(self):
290 id_, params = _build_data(self.apikey, 'invalidate_cache',
291 repoid=self.REPO)
292 response = api_call(self, params)
293
294 expected = ("Cache for repository `%s` was invalidated: "
295 "invalidated cache keys: %s" % (self.REPO,
296 [unicode(self.REPO)]))
297 self._compare_ok(id_, expected, given=response.body)
298
299 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
300 def test_api_invalidate_cache_error(self):
301 id_, params = _build_data(self.apikey, 'invalidate_cache',
302 repoid=self.REPO)
303 response = api_call(self, params)
304
305 expected = 'Error occurred during cache invalidation action'
306 self._compare_error(id_, expected, given=response.body)
307
289 308 def test_api_lock_repo_lock_aquire(self):
290 309 id_, params = _build_data(self.apikey, 'lock',
291 310 userid=TEST_USER_ADMIN_LOGIN,
292 311 repoid=self.REPO,
293 312 locked=True)
294 313 response = api_call(self, params)
295 314 expected = ('User `%s` set lock state for repo `%s` to `%s`'
296 315 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
297 316 self._compare_ok(id_, expected, given=response.body)
298 317
299 318 def test_api_lock_repo_lock_aquire_by_non_admin(self):
300 319 repo_name = 'api_delete_me'
301 320 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
302 321 try:
303 322 id_, params = _build_data(self.apikey_regular, 'lock',
304 323 repoid=repo_name,
305 324 locked=True)
306 325 response = api_call(self, params)
307 326 expected = ('User `%s` set lock state for repo `%s` to `%s`'
308 327 % (self.TEST_USER_LOGIN, repo_name, True))
309 328 self._compare_ok(id_, expected, given=response.body)
310 329 finally:
311 330 destroy_repo(repo_name)
312 331
313 332 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
314 333 repo_name = 'api_delete_me'
315 334 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
316 335 try:
317 336 id_, params = _build_data(self.apikey_regular, 'lock',
318 337 userid=TEST_USER_ADMIN_LOGIN,
319 338 repoid=repo_name,
320 339 locked=True)
321 340 response = api_call(self, params)
322 341 expected = 'userid is not the same as your user'
323 342 self._compare_error(id_, expected, given=response.body)
324 343 finally:
325 344 destroy_repo(repo_name)
326 345
327 346 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
328 347 id_, params = _build_data(self.apikey_regular, 'lock',
329 348 repoid=self.REPO,
330 349 locked=True)
331 350 response = api_call(self, params)
332 351 expected = 'repository `%s` does not exist' % (self.REPO)
333 352 self._compare_error(id_, expected, given=response.body)
334 353
335 354 def test_api_lock_repo_lock_release(self):
336 355 id_, params = _build_data(self.apikey, 'lock',
337 356 userid=TEST_USER_ADMIN_LOGIN,
338 357 repoid=self.REPO,
339 358 locked=False)
340 359 response = api_call(self, params)
341 360 expected = ('User `%s` set lock state for repo `%s` to `%s`'
342 361 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
343 362 self._compare_ok(id_, expected, given=response.body)
344 363
345 364 def test_api_lock_repo_lock_aquire_optional_userid(self):
346 365 id_, params = _build_data(self.apikey, 'lock',
347 366 repoid=self.REPO,
348 367 locked=True)
349 368 response = api_call(self, params)
350 369 expected = ('User `%s` set lock state for repo `%s` to `%s`'
351 370 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
352 371 self._compare_ok(id_, expected, given=response.body)
353 372
354 373 @mock.patch.object(Repository, 'lock', crash)
355 374 def test_api_lock_error(self):
356 375 id_, params = _build_data(self.apikey, 'lock',
357 376 userid=TEST_USER_ADMIN_LOGIN,
358 377 repoid=self.REPO,
359 378 locked=True)
360 379 response = api_call(self, params)
361 380
362 381 expected = 'Error occurred locking repository `%s`' % self.REPO
363 382 self._compare_error(id_, expected, given=response.body)
364 383
365 384 def test_api_create_existing_user(self):
366 385 id_, params = _build_data(self.apikey, 'create_user',
367 386 username=TEST_USER_ADMIN_LOGIN,
368 387 email='test@foo.com',
369 388 password='trololo')
370 389 response = api_call(self, params)
371 390
372 391 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
373 392 self._compare_error(id_, expected, given=response.body)
374 393
375 394 def test_api_create_user_with_existing_email(self):
376 395 id_, params = _build_data(self.apikey, 'create_user',
377 396 username=TEST_USER_ADMIN_LOGIN + 'new',
378 397 email=TEST_USER_REGULAR_EMAIL,
379 398 password='trololo')
380 399 response = api_call(self, params)
381 400
382 401 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
383 402 self._compare_error(id_, expected, given=response.body)
384 403
385 404 def test_api_create_user(self):
386 405 username = 'test_new_api_user'
387 406 email = username + "@foo.com"
388 407
389 408 id_, params = _build_data(self.apikey, 'create_user',
390 409 username=username,
391 410 email=email,
392 411 password='trololo')
393 412 response = api_call(self, params)
394 413
395 414 usr = UserModel().get_by_username(username)
396 415 ret = dict(
397 416 msg='created new user `%s`' % username,
398 417 user=jsonify(usr.get_api_data())
399 418 )
400 419
401 420 expected = ret
402 421 self._compare_ok(id_, expected, given=response.body)
403 422
404 423 UserModel().delete(usr.user_id)
405 424 Session().commit()
406 425
407 426 @mock.patch.object(UserModel, 'create_or_update', crash)
408 427 def test_api_create_user_when_exception_happened(self):
409 428
410 429 username = 'test_new_api_user'
411 430 email = username + "@foo.com"
412 431
413 432 id_, params = _build_data(self.apikey, 'create_user',
414 433 username=username,
415 434 email=email,
416 435 password='trololo')
417 436 response = api_call(self, params)
418 437 expected = 'failed to create user `%s`' % username
419 438 self._compare_error(id_, expected, given=response.body)
420 439
421 440 def test_api_delete_user(self):
422 441 usr = UserModel().create_or_update(username=u'test_user',
423 442 password=u'qweqwe',
424 443 email=u'u232@rhodecode.org',
425 444 firstname=u'u1', lastname=u'u1')
426 445 Session().commit()
427 446 username = usr.username
428 447 email = usr.email
429 448 usr_id = usr.user_id
430 449 ## DELETE THIS USER NOW
431 450
432 451 id_, params = _build_data(self.apikey, 'delete_user',
433 452 userid=username,)
434 453 response = api_call(self, params)
435 454
436 455 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
437 456 'user': None}
438 457 expected = ret
439 458 self._compare_ok(id_, expected, given=response.body)
440 459
441 460 @mock.patch.object(UserModel, 'delete', crash)
442 461 def test_api_delete_user_when_exception_happened(self):
443 462 usr = UserModel().create_or_update(username=u'test_user',
444 463 password=u'qweqwe',
445 464 email=u'u232@rhodecode.org',
446 465 firstname=u'u1', lastname=u'u1')
447 466 Session().commit()
448 467 username = usr.username
449 468
450 469 id_, params = _build_data(self.apikey, 'delete_user',
451 470 userid=username,)
452 471 response = api_call(self, params)
453 472 ret = 'failed to delete ID:%s %s' % (usr.user_id,
454 473 usr.username)
455 474 expected = ret
456 475 self._compare_error(id_, expected, given=response.body)
457 476
458 477 @parameterized.expand([('firstname', 'new_username'),
459 478 ('lastname', 'new_username'),
460 479 ('email', 'new_username'),
461 480 ('admin', True),
462 481 ('admin', False),
463 482 ('ldap_dn', 'test'),
464 483 ('ldap_dn', None),
465 484 ('active', False),
466 485 ('active', True),
467 486 ('password', 'newpass')
468 487 ])
469 488 def test_api_update_user(self, name, expected):
470 489 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
471 490 kw = {name: expected,
472 491 'userid': usr.user_id}
473 492 id_, params = _build_data(self.apikey, 'update_user', **kw)
474 493 response = api_call(self, params)
475 494
476 495 ret = {
477 496 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
478 497 'user': jsonify(UserModel()\
479 498 .get_by_username(self.TEST_USER_LOGIN)\
480 499 .get_api_data())
481 500 }
482 501
483 502 expected = ret
484 503 self._compare_ok(id_, expected, given=response.body)
485 504
486 505 def test_api_update_user_no_changed_params(self):
487 506 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
488 507 ret = jsonify(usr.get_api_data())
489 508 id_, params = _build_data(self.apikey, 'update_user',
490 509 userid=TEST_USER_ADMIN_LOGIN)
491 510
492 511 response = api_call(self, params)
493 512 ret = {
494 513 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
495 514 'user': ret
496 515 }
497 516 expected = ret
498 517 self._compare_ok(id_, expected, given=response.body)
499 518
500 519 def test_api_update_user_by_user_id(self):
501 520 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
502 521 ret = jsonify(usr.get_api_data())
503 522 id_, params = _build_data(self.apikey, 'update_user',
504 523 userid=usr.user_id)
505 524
506 525 response = api_call(self, params)
507 526 ret = {
508 527 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
509 528 'user': ret
510 529 }
511 530 expected = ret
512 531 self._compare_ok(id_, expected, given=response.body)
513 532
514 533 @mock.patch.object(UserModel, 'update_user', crash)
515 534 def test_api_update_user_when_exception_happens(self):
516 535 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
517 536 ret = jsonify(usr.get_api_data())
518 537 id_, params = _build_data(self.apikey, 'update_user',
519 538 userid=usr.user_id)
520 539
521 540 response = api_call(self, params)
522 541 ret = 'failed to update user `%s`' % usr.user_id
523 542
524 543 expected = ret
525 544 self._compare_error(id_, expected, given=response.body)
526 545
527 546 def test_api_get_repo(self):
528 547 new_group = 'some_new_group'
529 548 make_users_group(new_group)
530 549 RepoModel().grant_users_group_permission(repo=self.REPO,
531 550 group_name=new_group,
532 551 perm='repository.read')
533 552 Session().commit()
534 553 id_, params = _build_data(self.apikey, 'get_repo',
535 554 repoid=self.REPO)
536 555 response = api_call(self, params)
537 556
538 557 repo = RepoModel().get_by_repo_name(self.REPO)
539 558 ret = repo.get_api_data()
540 559
541 560 members = []
542 561 followers = []
543 562 for user in repo.repo_to_perm:
544 563 perm = user.permission.permission_name
545 564 user = user.user
546 565 user_data = user.get_api_data()
547 566 user_data['type'] = "user"
548 567 user_data['permission'] = perm
549 568 members.append(user_data)
550 569
551 570 for users_group in repo.users_group_to_perm:
552 571 perm = users_group.permission.permission_name
553 572 users_group = users_group.users_group
554 573 users_group_data = users_group.get_api_data()
555 574 users_group_data['type'] = "users_group"
556 575 users_group_data['permission'] = perm
557 576 members.append(users_group_data)
558 577
559 578 for user in repo.followers:
560 579 followers.append(user.user.get_api_data())
561 580
562 581 ret['members'] = members
563 582 ret['followers'] = followers
564 583
565 584 expected = ret
566 585 self._compare_ok(id_, expected, given=response.body)
567 586 destroy_users_group(new_group)
568 587
569 588 def test_api_get_repo_by_non_admin(self):
570 589 id_, params = _build_data(self.apikey, 'get_repo',
571 590 repoid=self.REPO)
572 591 response = api_call(self, params)
573 592
574 593 repo = RepoModel().get_by_repo_name(self.REPO)
575 594 ret = repo.get_api_data()
576 595
577 596 members = []
578 597 followers = []
579 598 for user in repo.repo_to_perm:
580 599 perm = user.permission.permission_name
581 600 user = user.user
582 601 user_data = user.get_api_data()
583 602 user_data['type'] = "user"
584 603 user_data['permission'] = perm
585 604 members.append(user_data)
586 605
587 606 for users_group in repo.users_group_to_perm:
588 607 perm = users_group.permission.permission_name
589 608 users_group = users_group.users_group
590 609 users_group_data = users_group.get_api_data()
591 610 users_group_data['type'] = "users_group"
592 611 users_group_data['permission'] = perm
593 612 members.append(users_group_data)
594 613
595 614 for user in repo.followers:
596 615 followers.append(user.user.get_api_data())
597 616
598 617 ret['members'] = members
599 618 ret['followers'] = followers
600 619
601 620 expected = ret
602 621 self._compare_ok(id_, expected, given=response.body)
603 622
604 623 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
605 624 RepoModel().grant_user_permission(repo=self.REPO,
606 625 user=self.TEST_USER_LOGIN,
607 626 perm='repository.none')
608 627
609 628 id_, params = _build_data(self.apikey_regular, 'get_repo',
610 629 repoid=self.REPO)
611 630 response = api_call(self, params)
612 631
613 632 expected = 'repository `%s` does not exist' % (self.REPO)
614 633 self._compare_error(id_, expected, given=response.body)
615 634
616 635 def test_api_get_repo_that_doesn_not_exist(self):
617 636 id_, params = _build_data(self.apikey, 'get_repo',
618 637 repoid='no-such-repo')
619 638 response = api_call(self, params)
620 639
621 640 ret = 'repository `%s` does not exist' % 'no-such-repo'
622 641 expected = ret
623 642 self._compare_error(id_, expected, given=response.body)
624 643
625 644 def test_api_get_repos(self):
626 645 id_, params = _build_data(self.apikey, 'get_repos')
627 646 response = api_call(self, params)
628 647
629 648 result = []
630 649 for repo in RepoModel().get_all():
631 650 result.append(repo.get_api_data())
632 651 ret = jsonify(result)
633 652
634 653 expected = ret
635 654 self._compare_ok(id_, expected, given=response.body)
636 655
637 656 def test_api_get_repos_non_admin(self):
638 657 id_, params = _build_data(self.apikey_regular, 'get_repos')
639 658 response = api_call(self, params)
640 659
641 660 result = []
642 661 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
643 662 result.append(repo.get_api_data())
644 663 ret = jsonify(result)
645 664
646 665 expected = ret
647 666 self._compare_ok(id_, expected, given=response.body)
648 667
649 668 @parameterized.expand([('all', 'all'),
650 669 ('dirs', 'dirs'),
651 670 ('files', 'files'), ])
652 671 def test_api_get_repo_nodes(self, name, ret_type):
653 672 rev = 'tip'
654 673 path = '/'
655 674 id_, params = _build_data(self.apikey, 'get_repo_nodes',
656 675 repoid=self.REPO, revision=rev,
657 676 root_path=path,
658 677 ret_type=ret_type)
659 678 response = api_call(self, params)
660 679
661 680 # we don't the actual return types here since it's tested somewhere
662 681 # else
663 682 expected = json.loads(response.body)['result']
664 683 self._compare_ok(id_, expected, given=response.body)
665 684
666 685 def test_api_get_repo_nodes_bad_revisions(self):
667 686 rev = 'i-dont-exist'
668 687 path = '/'
669 688 id_, params = _build_data(self.apikey, 'get_repo_nodes',
670 689 repoid=self.REPO, revision=rev,
671 690 root_path=path,)
672 691 response = api_call(self, params)
673 692
674 693 expected = 'failed to get repo: `%s` nodes' % self.REPO
675 694 self._compare_error(id_, expected, given=response.body)
676 695
677 696 def test_api_get_repo_nodes_bad_path(self):
678 697 rev = 'tip'
679 698 path = '/idontexits'
680 699 id_, params = _build_data(self.apikey, 'get_repo_nodes',
681 700 repoid=self.REPO, revision=rev,
682 701 root_path=path,)
683 702 response = api_call(self, params)
684 703
685 704 expected = 'failed to get repo: `%s` nodes' % self.REPO
686 705 self._compare_error(id_, expected, given=response.body)
687 706
688 707 def test_api_get_repo_nodes_bad_ret_type(self):
689 708 rev = 'tip'
690 709 path = '/'
691 710 ret_type = 'error'
692 711 id_, params = _build_data(self.apikey, 'get_repo_nodes',
693 712 repoid=self.REPO, revision=rev,
694 713 root_path=path,
695 714 ret_type=ret_type)
696 715 response = api_call(self, params)
697 716
698 717 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
699 718 self._compare_error(id_, expected, given=response.body)
700 719
701 720 def test_api_create_repo(self):
702 721 repo_name = 'api-repo'
703 722 id_, params = _build_data(self.apikey, 'create_repo',
704 723 repo_name=repo_name,
705 724 owner=TEST_USER_ADMIN_LOGIN,
706 725 repo_type='hg',
707 726 )
708 727 response = api_call(self, params)
709 728
710 729 repo = RepoModel().get_by_repo_name(repo_name)
711 730 ret = {
712 731 'msg': 'Created new repository `%s`' % repo_name,
713 732 'repo': jsonify(repo.get_api_data())
714 733 }
715 734 expected = ret
716 735 self._compare_ok(id_, expected, given=response.body)
717 736 destroy_repo(repo_name)
718 737
719 738 def test_api_create_repo_unknown_owner(self):
720 739 repo_name = 'api-repo'
721 740 owner = 'i-dont-exist'
722 741 id_, params = _build_data(self.apikey, 'create_repo',
723 742 repo_name=repo_name,
724 743 owner=owner,
725 744 repo_type='hg',
726 745 )
727 746 response = api_call(self, params)
728 747 expected = 'user `%s` does not exist' % owner
729 748 self._compare_error(id_, expected, given=response.body)
730 749
731 750 def test_api_create_repo_dont_specify_owner(self):
732 751 repo_name = 'api-repo'
733 752 owner = 'i-dont-exist'
734 753 id_, params = _build_data(self.apikey, 'create_repo',
735 754 repo_name=repo_name,
736 755 repo_type='hg',
737 756 )
738 757 response = api_call(self, params)
739 758
740 759 repo = RepoModel().get_by_repo_name(repo_name)
741 760 ret = {
742 761 'msg': 'Created new repository `%s`' % repo_name,
743 762 'repo': jsonify(repo.get_api_data())
744 763 }
745 764 expected = ret
746 765 self._compare_ok(id_, expected, given=response.body)
747 766 destroy_repo(repo_name)
748 767
749 768 def test_api_create_repo_by_non_admin(self):
750 769 repo_name = 'api-repo'
751 770 owner = 'i-dont-exist'
752 771 id_, params = _build_data(self.apikey_regular, 'create_repo',
753 772 repo_name=repo_name,
754 773 repo_type='hg',
755 774 )
756 775 response = api_call(self, params)
757 776
758 777 repo = RepoModel().get_by_repo_name(repo_name)
759 778 ret = {
760 779 'msg': 'Created new repository `%s`' % repo_name,
761 780 'repo': jsonify(repo.get_api_data())
762 781 }
763 782 expected = ret
764 783 self._compare_ok(id_, expected, given=response.body)
765 784 destroy_repo(repo_name)
766 785
767 786 def test_api_create_repo_by_non_admin_specify_owner(self):
768 787 repo_name = 'api-repo'
769 788 owner = 'i-dont-exist'
770 789 id_, params = _build_data(self.apikey_regular, 'create_repo',
771 790 repo_name=repo_name,
772 791 repo_type='hg',
773 792 owner=owner
774 793 )
775 794 response = api_call(self, params)
776 795
777 796 expected = 'Only RhodeCode admin can specify `owner` param'
778 797 self._compare_error(id_, expected, given=response.body)
779 798 destroy_repo(repo_name)
780 799
781 800 def test_api_create_repo_exists(self):
782 801 repo_name = self.REPO
783 802 id_, params = _build_data(self.apikey, 'create_repo',
784 803 repo_name=repo_name,
785 804 owner=TEST_USER_ADMIN_LOGIN,
786 805 repo_type='hg',
787 806 )
788 807 response = api_call(self, params)
789 808 expected = "repo `%s` already exist" % repo_name
790 809 self._compare_error(id_, expected, given=response.body)
791 810
792 811 @mock.patch.object(RepoModel, 'create_repo', crash)
793 812 def test_api_create_repo_exception_occurred(self):
794 813 repo_name = 'api-repo'
795 814 id_, params = _build_data(self.apikey, 'create_repo',
796 815 repo_name=repo_name,
797 816 owner=TEST_USER_ADMIN_LOGIN,
798 817 repo_type='hg',
799 818 )
800 819 response = api_call(self, params)
801 820 expected = 'failed to create repository `%s`' % repo_name
802 821 self._compare_error(id_, expected, given=response.body)
803 822
804 823 def test_api_delete_repo(self):
805 824 repo_name = 'api_delete_me'
806 825 create_repo(repo_name, self.REPO_TYPE)
807 826
808 827 id_, params = _build_data(self.apikey, 'delete_repo',
809 828 repoid=repo_name,)
810 829 response = api_call(self, params)
811 830
812 831 ret = {
813 832 'msg': 'Deleted repository `%s`' % repo_name,
814 833 'success': True
815 834 }
816 835 expected = ret
817 836 self._compare_ok(id_, expected, given=response.body)
818 837
819 838 def test_api_delete_repo_by_non_admin(self):
820 839 repo_name = 'api_delete_me'
821 840 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
822 841 try:
823 842 id_, params = _build_data(self.apikey_regular, 'delete_repo',
824 843 repoid=repo_name,)
825 844 response = api_call(self, params)
826 845
827 846 ret = {
828 847 'msg': 'Deleted repository `%s`' % repo_name,
829 848 'success': True
830 849 }
831 850 expected = ret
832 851 self._compare_ok(id_, expected, given=response.body)
833 852 finally:
834 853 destroy_repo(repo_name)
835 854
836 855 def test_api_delete_repo_by_non_admin_no_permission(self):
837 856 repo_name = 'api_delete_me'
838 857 create_repo(repo_name, self.REPO_TYPE)
839 858 try:
840 859 id_, params = _build_data(self.apikey_regular, 'delete_repo',
841 860 repoid=repo_name,)
842 861 response = api_call(self, params)
843 862 expected = 'repository `%s` does not exist' % (repo_name)
844 863 self._compare_error(id_, expected, given=response.body)
845 864 finally:
846 865 destroy_repo(repo_name)
847 866
848 867 def test_api_delete_repo_exception_occurred(self):
849 868 repo_name = 'api_delete_me'
850 869 create_repo(repo_name, self.REPO_TYPE)
851 870 try:
852 871 with mock.patch.object(RepoModel, 'delete', crash):
853 872 id_, params = _build_data(self.apikey, 'delete_repo',
854 873 repoid=repo_name,)
855 874 response = api_call(self, params)
856 875
857 876 expected = 'failed to delete repository `%s`' % repo_name
858 877 self._compare_error(id_, expected, given=response.body)
859 878 finally:
860 879 destroy_repo(repo_name)
861 880
862 881 def test_api_fork_repo(self):
863 882 fork_name = 'api-repo-fork'
864 883 id_, params = _build_data(self.apikey, 'fork_repo',
865 884 repoid=self.REPO,
866 885 fork_name=fork_name,
867 886 owner=TEST_USER_ADMIN_LOGIN,
868 887 )
869 888 response = api_call(self, params)
870 889
871 890 ret = {
872 891 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
873 892 fork_name),
874 893 'success': True
875 894 }
876 895 expected = ret
877 896 self._compare_ok(id_, expected, given=response.body)
878 897 destroy_repo(fork_name)
879 898
880 899 def test_api_fork_repo_non_admin(self):
881 900 fork_name = 'api-repo-fork'
882 901 id_, params = _build_data(self.apikey_regular, 'fork_repo',
883 902 repoid=self.REPO,
884 903 fork_name=fork_name,
885 904 )
886 905 response = api_call(self, params)
887 906
888 907 ret = {
889 908 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
890 909 fork_name),
891 910 'success': True
892 911 }
893 912 expected = ret
894 913 self._compare_ok(id_, expected, given=response.body)
895 914 destroy_repo(fork_name)
896 915
897 916 def test_api_fork_repo_non_admin_specify_owner(self):
898 917 fork_name = 'api-repo-fork'
899 918 id_, params = _build_data(self.apikey_regular, 'fork_repo',
900 919 repoid=self.REPO,
901 920 fork_name=fork_name,
902 921 owner=TEST_USER_ADMIN_LOGIN,
903 922 )
904 923 response = api_call(self, params)
905 924 expected = 'Only RhodeCode admin can specify `owner` param'
906 925 self._compare_error(id_, expected, given=response.body)
907 926 destroy_repo(fork_name)
908 927
909 928 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
910 929 RepoModel().grant_user_permission(repo=self.REPO,
911 930 user=self.TEST_USER_LOGIN,
912 931 perm='repository.none')
913 932 fork_name = 'api-repo-fork'
914 933 id_, params = _build_data(self.apikey_regular, 'fork_repo',
915 934 repoid=self.REPO,
916 935 fork_name=fork_name,
917 936 )
918 937 response = api_call(self, params)
919 938 expected = 'repository `%s` does not exist' % (self.REPO)
920 939 self._compare_error(id_, expected, given=response.body)
921 940 destroy_repo(fork_name)
922 941
923 942 def test_api_fork_repo_unknown_owner(self):
924 943 fork_name = 'api-repo-fork'
925 944 owner = 'i-dont-exist'
926 945 id_, params = _build_data(self.apikey, 'fork_repo',
927 946 repoid=self.REPO,
928 947 fork_name=fork_name,
929 948 owner=owner,
930 949 )
931 950 response = api_call(self, params)
932 951 expected = 'user `%s` does not exist' % owner
933 952 self._compare_error(id_, expected, given=response.body)
934 953
935 954 def test_api_fork_repo_fork_exists(self):
936 955 fork_name = 'api-repo-fork'
937 956 create_fork(fork_name, self.REPO_TYPE, self.REPO)
938 957
939 958 try:
940 959 fork_name = 'api-repo-fork'
941 960
942 961 id_, params = _build_data(self.apikey, 'fork_repo',
943 962 repoid=self.REPO,
944 963 fork_name=fork_name,
945 964 owner=TEST_USER_ADMIN_LOGIN,
946 965 )
947 966 response = api_call(self, params)
948 967
949 968 expected = "fork `%s` already exist" % fork_name
950 969 self._compare_error(id_, expected, given=response.body)
951 970 finally:
952 971 destroy_repo(fork_name)
953 972
954 973 def test_api_fork_repo_repo_exists(self):
955 974 fork_name = self.REPO
956 975
957 976 id_, params = _build_data(self.apikey, 'fork_repo',
958 977 repoid=self.REPO,
959 978 fork_name=fork_name,
960 979 owner=TEST_USER_ADMIN_LOGIN,
961 980 )
962 981 response = api_call(self, params)
963 982
964 983 expected = "repo `%s` already exist" % fork_name
965 984 self._compare_error(id_, expected, given=response.body)
966 985
967 986 @mock.patch.object(RepoModel, 'create_fork', crash)
968 987 def test_api_fork_repo_exception_occurred(self):
969 988 fork_name = 'api-repo-fork'
970 989 id_, params = _build_data(self.apikey, 'fork_repo',
971 990 repoid=self.REPO,
972 991 fork_name=fork_name,
973 992 owner=TEST_USER_ADMIN_LOGIN,
974 993 )
975 994 response = api_call(self, params)
976 995
977 996 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
978 997 fork_name)
979 998 self._compare_error(id_, expected, given=response.body)
980 999
981 1000 def test_api_get_users_group(self):
982 1001 id_, params = _build_data(self.apikey, 'get_users_group',
983 1002 usersgroupid=TEST_USERS_GROUP)
984 1003 response = api_call(self, params)
985 1004
986 1005 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
987 1006 members = []
988 1007 for user in users_group.members:
989 1008 user = user.user
990 1009 members.append(user.get_api_data())
991 1010
992 1011 ret = users_group.get_api_data()
993 1012 ret['members'] = members
994 1013 expected = ret
995 1014 self._compare_ok(id_, expected, given=response.body)
996 1015
997 1016 def test_api_get_users_groups(self):
998 1017
999 1018 make_users_group('test_users_group2')
1000 1019
1001 1020 id_, params = _build_data(self.apikey, 'get_users_groups',)
1002 1021 response = api_call(self, params)
1003 1022
1004 1023 expected = []
1005 1024 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
1006 1025 users_group = UsersGroupModel().get_group(gr_name)
1007 1026 ret = users_group.get_api_data()
1008 1027 expected.append(ret)
1009 1028 self._compare_ok(id_, expected, given=response.body)
1010 1029
1011 1030 UsersGroupModel().delete(users_group='test_users_group2')
1012 1031 Session().commit()
1013 1032
1014 1033 def test_api_create_users_group(self):
1015 1034 group_name = 'some_new_group'
1016 1035 id_, params = _build_data(self.apikey, 'create_users_group',
1017 1036 group_name=group_name)
1018 1037 response = api_call(self, params)
1019 1038
1020 1039 ret = {
1021 1040 'msg': 'created new users group `%s`' % group_name,
1022 1041 'users_group': jsonify(UsersGroupModel()\
1023 1042 .get_by_name(group_name)\
1024 1043 .get_api_data())
1025 1044 }
1026 1045 expected = ret
1027 1046 self._compare_ok(id_, expected, given=response.body)
1028 1047
1029 1048 destroy_users_group(group_name)
1030 1049
1031 1050 def test_api_get_users_group_that_exist(self):
1032 1051 id_, params = _build_data(self.apikey, 'create_users_group',
1033 1052 group_name=TEST_USERS_GROUP)
1034 1053 response = api_call(self, params)
1035 1054
1036 1055 expected = "users group `%s` already exist" % TEST_USERS_GROUP
1037 1056 self._compare_error(id_, expected, given=response.body)
1038 1057
1039 1058 @mock.patch.object(UsersGroupModel, 'create', crash)
1040 1059 def test_api_get_users_group_exception_occurred(self):
1041 1060 group_name = 'exception_happens'
1042 1061 id_, params = _build_data(self.apikey, 'create_users_group',
1043 1062 group_name=group_name)
1044 1063 response = api_call(self, params)
1045 1064
1046 1065 expected = 'failed to create group `%s`' % group_name
1047 1066 self._compare_error(id_, expected, given=response.body)
1048 1067
1049 1068 def test_api_add_user_to_users_group(self):
1050 1069 gr_name = 'test_group'
1051 1070 UsersGroupModel().create(gr_name)
1052 1071 Session().commit()
1053 1072 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1054 1073 usersgroupid=gr_name,
1055 1074 userid=TEST_USER_ADMIN_LOGIN)
1056 1075 response = api_call(self, params)
1057 1076
1058 1077 expected = {
1059 1078 'msg': 'added member `%s` to users group `%s`' % (
1060 1079 TEST_USER_ADMIN_LOGIN, gr_name
1061 1080 ),
1062 1081 'success': True}
1063 1082 self._compare_ok(id_, expected, given=response.body)
1064 1083
1065 1084 UsersGroupModel().delete(users_group=gr_name)
1066 1085 Session().commit()
1067 1086
1068 1087 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1069 1088 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1070 1089 usersgroupid='false-group',
1071 1090 userid=TEST_USER_ADMIN_LOGIN)
1072 1091 response = api_call(self, params)
1073 1092
1074 1093 expected = 'users group `%s` does not exist' % 'false-group'
1075 1094 self._compare_error(id_, expected, given=response.body)
1076 1095
1077 1096 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
1078 1097 def test_api_add_user_to_users_group_exception_occurred(self):
1079 1098 gr_name = 'test_group'
1080 1099 UsersGroupModel().create(gr_name)
1081 1100 Session().commit()
1082 1101 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1083 1102 usersgroupid=gr_name,
1084 1103 userid=TEST_USER_ADMIN_LOGIN)
1085 1104 response = api_call(self, params)
1086 1105
1087 1106 expected = 'failed to add member to users group `%s`' % gr_name
1088 1107 self._compare_error(id_, expected, given=response.body)
1089 1108
1090 1109 UsersGroupModel().delete(users_group=gr_name)
1091 1110 Session().commit()
1092 1111
1093 1112 def test_api_remove_user_from_users_group(self):
1094 1113 gr_name = 'test_group_3'
1095 1114 gr = UsersGroupModel().create(gr_name)
1096 1115 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1097 1116 Session().commit()
1098 1117 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1099 1118 usersgroupid=gr_name,
1100 1119 userid=TEST_USER_ADMIN_LOGIN)
1101 1120 response = api_call(self, params)
1102 1121
1103 1122 expected = {
1104 1123 'msg': 'removed member `%s` from users group `%s`' % (
1105 1124 TEST_USER_ADMIN_LOGIN, gr_name
1106 1125 ),
1107 1126 'success': True}
1108 1127 self._compare_ok(id_, expected, given=response.body)
1109 1128
1110 1129 UsersGroupModel().delete(users_group=gr_name)
1111 1130 Session().commit()
1112 1131
1113 1132 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
1114 1133 def test_api_remove_user_from_users_group_exception_occurred(self):
1115 1134 gr_name = 'test_group_3'
1116 1135 gr = UsersGroupModel().create(gr_name)
1117 1136 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1118 1137 Session().commit()
1119 1138 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1120 1139 usersgroupid=gr_name,
1121 1140 userid=TEST_USER_ADMIN_LOGIN)
1122 1141 response = api_call(self, params)
1123 1142
1124 1143 expected = 'failed to remove member from users group `%s`' % gr_name
1125 1144 self._compare_error(id_, expected, given=response.body)
1126 1145
1127 1146 UsersGroupModel().delete(users_group=gr_name)
1128 1147 Session().commit()
1129 1148
1130 1149 @parameterized.expand([('none', 'repository.none'),
1131 1150 ('read', 'repository.read'),
1132 1151 ('write', 'repository.write'),
1133 1152 ('admin', 'repository.admin')])
1134 1153 def test_api_grant_user_permission(self, name, perm):
1135 1154 id_, params = _build_data(self.apikey, 'grant_user_permission',
1136 1155 repoid=self.REPO,
1137 1156 userid=TEST_USER_ADMIN_LOGIN,
1138 1157 perm=perm)
1139 1158 response = api_call(self, params)
1140 1159
1141 1160 ret = {
1142 1161 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1143 1162 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1144 1163 ),
1145 1164 'success': True
1146 1165 }
1147 1166 expected = ret
1148 1167 self._compare_ok(id_, expected, given=response.body)
1149 1168
1150 1169 def test_api_grant_user_permission_wrong_permission(self):
1151 1170 perm = 'haha.no.permission'
1152 1171 id_, params = _build_data(self.apikey, 'grant_user_permission',
1153 1172 repoid=self.REPO,
1154 1173 userid=TEST_USER_ADMIN_LOGIN,
1155 1174 perm=perm)
1156 1175 response = api_call(self, params)
1157 1176
1158 1177 expected = 'permission `%s` does not exist' % perm
1159 1178 self._compare_error(id_, expected, given=response.body)
1160 1179
1161 1180 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1162 1181 def test_api_grant_user_permission_exception_when_adding(self):
1163 1182 perm = 'repository.read'
1164 1183 id_, params = _build_data(self.apikey, 'grant_user_permission',
1165 1184 repoid=self.REPO,
1166 1185 userid=TEST_USER_ADMIN_LOGIN,
1167 1186 perm=perm)
1168 1187 response = api_call(self, params)
1169 1188
1170 1189 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1171 1190 TEST_USER_ADMIN_LOGIN, self.REPO
1172 1191 )
1173 1192 self._compare_error(id_, expected, given=response.body)
1174 1193
1175 1194 def test_api_revoke_user_permission(self):
1176 1195 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1177 1196 repoid=self.REPO,
1178 1197 userid=TEST_USER_ADMIN_LOGIN,)
1179 1198 response = api_call(self, params)
1180 1199
1181 1200 expected = {
1182 1201 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1183 1202 TEST_USER_ADMIN_LOGIN, self.REPO
1184 1203 ),
1185 1204 'success': True
1186 1205 }
1187 1206 self._compare_ok(id_, expected, given=response.body)
1188 1207
1189 1208 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1190 1209 def test_api_revoke_user_permission_exception_when_adding(self):
1191 1210 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1192 1211 repoid=self.REPO,
1193 1212 userid=TEST_USER_ADMIN_LOGIN,)
1194 1213 response = api_call(self, params)
1195 1214
1196 1215 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1197 1216 TEST_USER_ADMIN_LOGIN, self.REPO
1198 1217 )
1199 1218 self._compare_error(id_, expected, given=response.body)
1200 1219
1201 1220 @parameterized.expand([('none', 'repository.none'),
1202 1221 ('read', 'repository.read'),
1203 1222 ('write', 'repository.write'),
1204 1223 ('admin', 'repository.admin')])
1205 1224 def test_api_grant_users_group_permission(self, name, perm):
1206 1225 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1207 1226 repoid=self.REPO,
1208 1227 usersgroupid=TEST_USERS_GROUP,
1209 1228 perm=perm)
1210 1229 response = api_call(self, params)
1211 1230
1212 1231 ret = {
1213 1232 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
1214 1233 perm, TEST_USERS_GROUP, self.REPO
1215 1234 ),
1216 1235 'success': True
1217 1236 }
1218 1237 expected = ret
1219 1238 self._compare_ok(id_, expected, given=response.body)
1220 1239
1221 1240 def test_api_grant_users_group_permission_wrong_permission(self):
1222 1241 perm = 'haha.no.permission'
1223 1242 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1224 1243 repoid=self.REPO,
1225 1244 usersgroupid=TEST_USERS_GROUP,
1226 1245 perm=perm)
1227 1246 response = api_call(self, params)
1228 1247
1229 1248 expected = 'permission `%s` does not exist' % perm
1230 1249 self._compare_error(id_, expected, given=response.body)
1231 1250
1232 1251 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1233 1252 def test_api_grant_users_group_permission_exception_when_adding(self):
1234 1253 perm = 'repository.read'
1235 1254 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1236 1255 repoid=self.REPO,
1237 1256 usersgroupid=TEST_USERS_GROUP,
1238 1257 perm=perm)
1239 1258 response = api_call(self, params)
1240 1259
1241 1260 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
1242 1261 TEST_USERS_GROUP, self.REPO
1243 1262 )
1244 1263 self._compare_error(id_, expected, given=response.body)
1245 1264
1246 1265 def test_api_revoke_users_group_permission(self):
1247 1266 RepoModel().grant_users_group_permission(repo=self.REPO,
1248 1267 group_name=TEST_USERS_GROUP,
1249 1268 perm='repository.read')
1250 1269 Session().commit()
1251 1270 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1252 1271 repoid=self.REPO,
1253 1272 usersgroupid=TEST_USERS_GROUP,)
1254 1273 response = api_call(self, params)
1255 1274
1256 1275 expected = {
1257 1276 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
1258 1277 TEST_USERS_GROUP, self.REPO
1259 1278 ),
1260 1279 'success': True
1261 1280 }
1262 1281 self._compare_ok(id_, expected, given=response.body)
1263 1282
1264 1283 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1265 1284 def test_api_revoke_users_group_permission_exception_when_adding(self):
1266 1285
1267 1286 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1268 1287 repoid=self.REPO,
1269 1288 usersgroupid=TEST_USERS_GROUP,)
1270 1289 response = api_call(self, params)
1271 1290
1272 1291 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
1273 1292 TEST_USERS_GROUP, self.REPO
1274 1293 )
1275 1294 self._compare_error(id_, expected, given=response.body)
General Comments 0
You need to be logged in to leave comments. Login now