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