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