##// END OF EJS Templates
Implemented API calls for non-admin users for locking/unlocking repositories
marcink -
r3161:3563c47e beta
parent child Browse files
Show More
@@ -1,928 +1,929 b''
1 .. _api:
1 .. _api:
2
2
3 ===
3 ===
4 API
4 API
5 ===
5 ===
6
6
7
7
8 Starting from RhodeCode version 1.2 a simple API was implemented.
8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 There's a single schema for calling all api methods. API is implemented
9 There's a single schema for calling all api methods. API is implemented
10 with JSON protocol both ways. An url to send API request to RhodeCode is
10 with JSON protocol both ways. An url to send API request to RhodeCode is
11 <your_server>/_admin/api
11 <your_server>/_admin/api
12
12
13 API ACCESS FOR WEB VIEWS
13 API ACCESS FOR WEB VIEWS
14 ++++++++++++++++++++++++
14 ++++++++++++++++++++++++
15
15
16 API access can also be turned on for each web view in RhodeCode that is
16 API access can also be turned on for each web view in RhodeCode that is
17 decorated with `@LoginRequired` decorator. To enable API access simple change
17 decorated with `@LoginRequired` decorator. To enable API access simple change
18 the standard login decorator to `@LoginRequired(api_access=True)`.
18 the standard login decorator to `@LoginRequired(api_access=True)`.
19 After this change, a rhodecode view can be accessed without login by adding a
19 After this change, a rhodecode view can be accessed without login by adding a
20 GET parameter `?api_key=<api_key>` to url. By default this is only
20 GET parameter `?api_key=<api_key>` to url. By default this is only
21 enabled on RSS/ATOM feed views.
21 enabled on RSS/ATOM feed views.
22
22
23
23
24 API ACCESS
24 API ACCESS
25 ++++++++++
25 ++++++++++
26
26
27 All clients are required to send JSON-RPC spec JSON data::
27 All clients are required to send JSON-RPC spec JSON data::
28
28
29 {
29 {
30 "id:"<id>",
30 "id:"<id>",
31 "api_key":"<api_key>",
31 "api_key":"<api_key>",
32 "method":"<method_name>",
32 "method":"<method_name>",
33 "args":{"<arg_key>":"<arg_val>"}
33 "args":{"<arg_key>":"<arg_val>"}
34 }
34 }
35
35
36 Example call for autopulling remotes repos using curl::
36 Example call for autopulling remotes repos using curl::
37 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
37 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
38
38
39 Simply provide
39 Simply provide
40 - *id* A value of any type, which is used to match the response with the request that it is replying to.
40 - *id* A value of any type, which is used to match the response with the request that it is replying to.
41 - *api_key* for access and permission validation.
41 - *api_key* for access and permission validation.
42 - *method* is name of method to call
42 - *method* is name of method to call
43 - *args* is an key:value list of arguments to pass to method
43 - *args* is an key:value list of arguments to pass to method
44
44
45 .. note::
45 .. note::
46
46
47 api_key can be found in your user account page
47 api_key can be found in your user account page
48
48
49
49
50 RhodeCode API will return always a JSON-RPC response::
50 RhodeCode API will return always a JSON-RPC response::
51
51
52 {
52 {
53 "id":<id>, # matching id sent by request
53 "id":<id>, # matching id sent by request
54 "result": "<result>"|null, # JSON formatted result, null if any errors
54 "result": "<result>"|null, # JSON formatted result, null if any errors
55 "error": "null"|<error_message> # JSON formatted error (if any)
55 "error": "null"|<error_message> # JSON formatted error (if any)
56 }
56 }
57
57
58 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
58 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
59 calling api *error* key from response will contain failure description
59 calling api *error* key from response will contain failure description
60 and result will be null.
60 and result will be null.
61
61
62
62
63 API CLIENT
63 API CLIENT
64 ++++++++++
64 ++++++++++
65
65
66 From version 1.4 RhodeCode adds a script that allows to easily
66 From version 1.4 RhodeCode adds a script that allows to easily
67 communicate with API. After installing RhodeCode a `rhodecode-api` script
67 communicate with API. After installing RhodeCode a `rhodecode-api` script
68 will be available.
68 will be available.
69
69
70 To get started quickly simply run::
70 To get started quickly simply run::
71
71
72 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
72 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
73
73
74 This will create a file named .config in the directory you executed it storing
74 This will create a file named .config in the directory you executed it storing
75 json config file with credentials. You can skip this step and always provide
75 json config file with credentials. You can skip this step and always provide
76 both of the arguments to be able to communicate with server
76 both of the arguments to be able to communicate with server
77
77
78
78
79 after that simply run any api command for example get_repo::
79 after that simply run any api command for example get_repo::
80
80
81 rhodecode-api get_repo
81 rhodecode-api get_repo
82
82
83 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
83 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
84 rhodecode said:
84 rhodecode said:
85 {'error': 'Missing non optional `repoid` arg in JSON DATA',
85 {'error': 'Missing non optional `repoid` arg in JSON DATA',
86 'id': 75,
86 'id': 75,
87 'result': None}
87 'result': None}
88
88
89 Ups looks like we forgot to add an argument
89 Ups looks like we forgot to add an argument
90
90
91 Let's try again now giving the repoid as parameters::
91 Let's try again now giving the repoid as parameters::
92
92
93 rhodecode-api get_repo repoid:rhodecode
93 rhodecode-api get_repo repoid:rhodecode
94
94
95 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
95 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
96 rhodecode said:
96 rhodecode said:
97 {'error': None,
97 {'error': None,
98 'id': 39,
98 'id': 39,
99 'result': <json data...>}
99 'result': <json data...>}
100
100
101
101
102
102
103 API METHODS
103 API METHODS
104 +++++++++++
104 +++++++++++
105
105
106
106
107 pull
107 pull
108 ----
108 ----
109
109
110 Pulls given repo from remote location. Can be used to automatically keep
110 Pulls given repo from remote location. Can be used to automatically keep
111 remote repos up to date. This command can be executed only using api_key
111 remote repos up to date. This command can be executed only using api_key
112 belonging to user with admin rights
112 belonging to user with admin rights
113
113
114 INPUT::
114 INPUT::
115
115
116 id : <id_for_response>
116 id : <id_for_response>
117 api_key : "<api_key>"
117 api_key : "<api_key>"
118 method : "pull"
118 method : "pull"
119 args : {
119 args : {
120 "repoid" : "<reponame or repo_id>"
120 "repoid" : "<reponame or repo_id>"
121 }
121 }
122
122
123 OUTPUT::
123 OUTPUT::
124
124
125 id : <id_given_in_input>
125 id : <id_given_in_input>
126 result : "Pulled from `<reponame>`"
126 result : "Pulled from `<reponame>`"
127 error : null
127 error : null
128
128
129
129
130 rescan_repos
130 rescan_repos
131 ------------
131 ------------
132
132
133 Dispatch rescan repositories action. If remove_obsolete is set
133 Dispatch rescan repositories action. If remove_obsolete is set
134 RhodeCode will delete repos that are in database but not in the filesystem.
134 RhodeCode will delete repos that are in database but not in the filesystem.
135 This command can be executed only using api_key belonging to user with admin
135 This command can be executed only using api_key belonging to user with admin
136 rights.
136 rights.
137
137
138 INPUT::
138 INPUT::
139
139
140 id : <id_for_response>
140 id : <id_for_response>
141 api_key : "<api_key>"
141 api_key : "<api_key>"
142 method : "rescan_repos"
142 method : "rescan_repos"
143 args : {
143 args : {
144 "remove_obsolete" : "<boolean = Optional(False)>"
144 "remove_obsolete" : "<boolean = Optional(False)>"
145 }
145 }
146
146
147 OUTPUT::
147 OUTPUT::
148
148
149 id : <id_given_in_input>
149 id : <id_given_in_input>
150 result : "{'added': [<list of names of added repos>],
150 result : "{'added': [<list of names of added repos>],
151 'removed': [<list of names of removed repos>]}"
151 'removed': [<list of names of removed repos>]}"
152 error : null
152 error : null
153
153
154
154
155 lock
155 lock
156 ----
156 ----
157
157
158 Set locking state on given repository by given user.
158 Set locking state on given repository by given user. If userid param is skipped
159 , then it is set to id of user whos calling this method.
159 This command can be executed only using api_key belonging to user with admin
160 This command can be executed only using api_key belonging to user with admin
160 rights.
161 rights or regular user that have admin or write access to repository.
161
162
162 INPUT::
163 INPUT::
163
164
164 id : <id_for_response>
165 id : <id_for_response>
165 api_key : "<api_key>"
166 api_key : "<api_key>"
166 method : "lock"
167 method : "lock"
167 args : {
168 args : {
168 "repoid" : "<reponame or repo_id>"
169 "repoid" : "<reponame or repo_id>"
169 "userid" : "<user_id or username>",
170 "userid" : "<user_id or username = Optional(=apiuser)>",
170 "locked" : "<bool true|false>"
171 "locked" : "<bool true|false>"
171 }
172 }
172
173
173 OUTPUT::
174 OUTPUT::
174
175
175 id : <id_given_in_input>
176 id : <id_given_in_input>
176 result : "User `<username>` set lock state for repo `<reponame>` to `true|false`"
177 result : "User `<username>` set lock state for repo `<reponame>` to `true|false`"
177 error : null
178 error : null
178
179
179
180
180 show_ip
181 show_ip
181 -------
182 -------
182
183
183 Shows IP address as seen from RhodeCode server, together with all
184 Shows IP address as seen from RhodeCode server, together with all
184 defined IP addresses for given user.
185 defined IP addresses for given user.
185 This command can be executed only using api_key belonging to user with admin
186 This command can be executed only using api_key belonging to user with admin
186 rights.
187 rights.
187
188
188 INPUT::
189 INPUT::
189
190
190 id : <id_for_response>
191 id : <id_for_response>
191 api_key : "<api_key>"
192 api_key : "<api_key>"
192 method : "show_ip"
193 method : "show_ip"
193 args : {
194 args : {
194 "userid" : "<user_id or username>",
195 "userid" : "<user_id or username>",
195 }
196 }
196
197
197 OUTPUT::
198 OUTPUT::
198
199
199 id : <id_given_in_input>
200 id : <id_given_in_input>
200 result : {
201 result : {
201 "ip_addr_server": <ip_from_clien>",
202 "ip_addr_server": <ip_from_clien>",
202 "user_ips": [
203 "user_ips": [
203 {
204 {
204 "ip_addr": "<ip_with_mask>",
205 "ip_addr": "<ip_with_mask>",
205 "ip_range": ["<start_ip>", "<end_ip>"],
206 "ip_range": ["<start_ip>", "<end_ip>"],
206 },
207 },
207 ...
208 ...
208 ]
209 ]
209 }
210 }
210
211
211 error : null
212 error : null
212
213
213
214
214 get_user
215 get_user
215 --------
216 --------
216
217
217 Get's an user by username or user_id, Returns empty result if user is not found.
218 Get's an user by username or user_id, Returns empty result if user is not found.
218 This command can be executed only using api_key belonging to user with admin
219 This command can be executed only using api_key belonging to user with admin
219 rights.
220 rights.
220
221
221
222
222 INPUT::
223 INPUT::
223
224
224 id : <id_for_response>
225 id : <id_for_response>
225 api_key : "<api_key>"
226 api_key : "<api_key>"
226 method : "get_user"
227 method : "get_user"
227 args : {
228 args : {
228 "userid" : "<username or user_id>"
229 "userid" : "<username or user_id>"
229 }
230 }
230
231
231 OUTPUT::
232 OUTPUT::
232
233
233 id : <id_given_in_input>
234 id : <id_given_in_input>
234 result: None if user does not exist or
235 result: None if user does not exist or
235 {
236 {
236 "user_id" : "<user_id>",
237 "user_id" : "<user_id>",
237 "username" : "<username>",
238 "username" : "<username>",
238 "firstname": "<firstname>",
239 "firstname": "<firstname>",
239 "lastname" : "<lastname>",
240 "lastname" : "<lastname>",
240 "email" : "<email>",
241 "email" : "<email>",
241 "emails": "<list_of_all_additional_emails>",
242 "emails": "<list_of_all_additional_emails>",
242 "ip_addresses": "<list_of_ip_addresses_for_user>",
243 "ip_addresses": "<list_of_ip_addresses_for_user>",
243 "active" : "<bool>",
244 "active" : "<bool>",
244 "admin" :Β  "<bool>",
245 "admin" :Β  "<bool>",
245 "ldap_dn" : "<ldap_dn>",
246 "ldap_dn" : "<ldap_dn>",
246 "last_login": "<last_login>",
247 "last_login": "<last_login>",
247 "permissions": {
248 "permissions": {
248 "global": ["hg.create.repository",
249 "global": ["hg.create.repository",
249 "repository.read",
250 "repository.read",
250 "hg.register.manual_activate"],
251 "hg.register.manual_activate"],
251 "repositories": {"repo1": "repository.none"},
252 "repositories": {"repo1": "repository.none"},
252 "repositories_groups": {"Group1": "group.read"}
253 "repositories_groups": {"Group1": "group.read"}
253 },
254 },
254 }
255 }
255
256
256 error: null
257 error: null
257
258
258
259
259 get_users
260 get_users
260 ---------
261 ---------
261
262
262 Lists all existing users. This command can be executed only using api_key
263 Lists all existing users. This command can be executed only using api_key
263 belonging to user with admin rights.
264 belonging to user with admin rights.
264
265
265
266
266 INPUT::
267 INPUT::
267
268
268 id : <id_for_response>
269 id : <id_for_response>
269 api_key : "<api_key>"
270 api_key : "<api_key>"
270 method : "get_users"
271 method : "get_users"
271 args : { }
272 args : { }
272
273
273 OUTPUT::
274 OUTPUT::
274
275
275 id : <id_given_in_input>
276 id : <id_given_in_input>
276 result: [
277 result: [
277 {
278 {
278 "user_id" : "<user_id>",
279 "user_id" : "<user_id>",
279 "username" : "<username>",
280 "username" : "<username>",
280 "firstname": "<firstname>",
281 "firstname": "<firstname>",
281 "lastname" : "<lastname>",
282 "lastname" : "<lastname>",
282 "email" : "<email>",
283 "email" : "<email>",
283 "emails": "<list_of_all_additional_emails>",
284 "emails": "<list_of_all_additional_emails>",
284 "ip_addresses": "<list_of_ip_addresses_for_user>",
285 "ip_addresses": "<list_of_ip_addresses_for_user>",
285 "active" : "<bool>",
286 "active" : "<bool>",
286 "admin" :Β  "<bool>",
287 "admin" :Β  "<bool>",
287 "ldap_dn" : "<ldap_dn>",
288 "ldap_dn" : "<ldap_dn>",
288 "last_login": "<last_login>",
289 "last_login": "<last_login>",
289 },
290 },
290 …
291 …
291 ]
292 ]
292 error: null
293 error: null
293
294
294
295
295 create_user
296 create_user
296 -----------
297 -----------
297
298
298 Creates new user. This command can
299 Creates new user. This command can
299 be executed only using api_key belonging to user with admin rights.
300 be executed only using api_key belonging to user with admin rights.
300
301
301
302
302 INPUT::
303 INPUT::
303
304
304 id : <id_for_response>
305 id : <id_for_response>
305 api_key : "<api_key>"
306 api_key : "<api_key>"
306 method : "create_user"
307 method : "create_user"
307 args : {
308 args : {
308 "username" : "<username>",
309 "username" : "<username>",
309 "email" : "<useremail>",
310 "email" : "<useremail>",
310 "password" : "<password>",
311 "password" : "<password>",
311 "firstname" : "<firstname> = Optional(None)",
312 "firstname" : "<firstname> = Optional(None)",
312 "lastname" : "<lastname> = Optional(None)",
313 "lastname" : "<lastname> = Optional(None)",
313 "active" : "<bool> = Optional(True)",
314 "active" : "<bool> = Optional(True)",
314 "admin" : "<bool> = Optional(False)",
315 "admin" : "<bool> = Optional(False)",
315 "ldap_dn" : "<ldap_dn> = Optional(None)"
316 "ldap_dn" : "<ldap_dn> = Optional(None)"
316 }
317 }
317
318
318 OUTPUT::
319 OUTPUT::
319
320
320 id : <id_given_in_input>
321 id : <id_given_in_input>
321 result: {
322 result: {
322 "msg" : "created new user `<username>`",
323 "msg" : "created new user `<username>`",
323 "user": {
324 "user": {
324 "user_id" : "<user_id>",
325 "user_id" : "<user_id>",
325 "username" : "<username>",
326 "username" : "<username>",
326 "firstname": "<firstname>",
327 "firstname": "<firstname>",
327 "lastname" : "<lastname>",
328 "lastname" : "<lastname>",
328 "email" : "<email>",
329 "email" : "<email>",
329 "emails": "<list_of_all_additional_emails>",
330 "emails": "<list_of_all_additional_emails>",
330 "active" : "<bool>",
331 "active" : "<bool>",
331 "admin" :Β  "<bool>",
332 "admin" :Β  "<bool>",
332 "ldap_dn" : "<ldap_dn>",
333 "ldap_dn" : "<ldap_dn>",
333 "last_login": "<last_login>",
334 "last_login": "<last_login>",
334 },
335 },
335 }
336 }
336 error: null
337 error: null
337
338
338
339
339 update_user
340 update_user
340 -----------
341 -----------
341
342
342 updates given user if such user exists. This command can
343 updates given user if such user exists. This command can
343 be executed only using api_key belonging to user with admin rights.
344 be executed only using api_key belonging to user with admin rights.
344
345
345
346
346 INPUT::
347 INPUT::
347
348
348 id : <id_for_response>
349 id : <id_for_response>
349 api_key : "<api_key>"
350 api_key : "<api_key>"
350 method : "update_user"
351 method : "update_user"
351 args : {
352 args : {
352 "userid" : "<user_id or username>",
353 "userid" : "<user_id or username>",
353 "username" : "<username> = Optional",
354 "username" : "<username> = Optional",
354 "email" : "<useremail> = Optional",
355 "email" : "<useremail> = Optional",
355 "password" : "<password> = Optional",
356 "password" : "<password> = Optional",
356 "firstname" : "<firstname> = Optional",
357 "firstname" : "<firstname> = Optional",
357 "lastname" : "<lastname> = Optional",
358 "lastname" : "<lastname> = Optional",
358 "active" : "<bool> = Optional",
359 "active" : "<bool> = Optional",
359 "admin" : "<bool> = Optional",
360 "admin" : "<bool> = Optional",
360 "ldap_dn" : "<ldap_dn> = Optional"
361 "ldap_dn" : "<ldap_dn> = Optional"
361 }
362 }
362
363
363 OUTPUT::
364 OUTPUT::
364
365
365 id : <id_given_in_input>
366 id : <id_given_in_input>
366 result: {
367 result: {
367 "msg" : "updated user ID:<userid> <username>",
368 "msg" : "updated user ID:<userid> <username>",
368 "user": {
369 "user": {
369 "user_id" : "<user_id>",
370 "user_id" : "<user_id>",
370 "username" : "<username>",
371 "username" : "<username>",
371 "firstname": "<firstname>",
372 "firstname": "<firstname>",
372 "lastname" : "<lastname>",
373 "lastname" : "<lastname>",
373 "email" : "<email>",
374 "email" : "<email>",
374 "emails": "<list_of_all_additional_emails>",
375 "emails": "<list_of_all_additional_emails>",
375 "active" : "<bool>",
376 "active" : "<bool>",
376 "admin" :Β  "<bool>",
377 "admin" :Β  "<bool>",
377 "ldap_dn" : "<ldap_dn>",
378 "ldap_dn" : "<ldap_dn>",
378 "last_login": "<last_login>",
379 "last_login": "<last_login>",
379 },
380 },
380 }
381 }
381 error: null
382 error: null
382
383
383
384
384 delete_user
385 delete_user
385 -----------
386 -----------
386
387
387
388
388 deletes givenuser if such user exists. This command can
389 deletes givenuser if such user exists. This command can
389 be executed only using api_key belonging to user with admin rights.
390 be executed only using api_key belonging to user with admin rights.
390
391
391
392
392 INPUT::
393 INPUT::
393
394
394 id : <id_for_response>
395 id : <id_for_response>
395 api_key : "<api_key>"
396 api_key : "<api_key>"
396 method : "delete_user"
397 method : "delete_user"
397 args : {
398 args : {
398 "userid" : "<user_id or username>",
399 "userid" : "<user_id or username>",
399 }
400 }
400
401
401 OUTPUT::
402 OUTPUT::
402
403
403 id : <id_given_in_input>
404 id : <id_given_in_input>
404 result: {
405 result: {
405 "msg" : "deleted user ID:<userid> <username>",
406 "msg" : "deleted user ID:<userid> <username>",
406 "user": null
407 "user": null
407 }
408 }
408 error: null
409 error: null
409
410
410
411
411 get_users_group
412 get_users_group
412 ---------------
413 ---------------
413
414
414 Gets an existing users group. This command can be executed only using api_key
415 Gets an existing users group. This command can be executed only using api_key
415 belonging to user with admin rights.
416 belonging to user with admin rights.
416
417
417
418
418 INPUT::
419 INPUT::
419
420
420 id : <id_for_response>
421 id : <id_for_response>
421 api_key : "<api_key>"
422 api_key : "<api_key>"
422 method : "get_users_group"
423 method : "get_users_group"
423 args : {
424 args : {
424 "usersgroupid" : "<users group id or name>"
425 "usersgroupid" : "<users group id or name>"
425 }
426 }
426
427
427 OUTPUT::
428 OUTPUT::
428
429
429 id : <id_given_in_input>
430 id : <id_given_in_input>
430 result : None if group not exist
431 result : None if group not exist
431 {
432 {
432 "users_group_id" : "<id>",
433 "users_group_id" : "<id>",
433 "group_name" : "<groupname>",
434 "group_name" : "<groupname>",
434 "active": "<bool>",
435 "active": "<bool>",
435 "members" : [
436 "members" : [
436 {
437 {
437 "user_id" : "<user_id>",
438 "user_id" : "<user_id>",
438 "username" : "<username>",
439 "username" : "<username>",
439 "firstname": "<firstname>",
440 "firstname": "<firstname>",
440 "lastname" : "<lastname>",
441 "lastname" : "<lastname>",
441 "email" : "<email>",
442 "email" : "<email>",
442 "emails": "<list_of_all_additional_emails>",
443 "emails": "<list_of_all_additional_emails>",
443 "active" : "<bool>",
444 "active" : "<bool>",
444 "admin" :Β  "<bool>",
445 "admin" :Β  "<bool>",
445 "ldap_dn" : "<ldap_dn>",
446 "ldap_dn" : "<ldap_dn>",
446 "last_login": "<last_login>",
447 "last_login": "<last_login>",
447 },
448 },
448 …
449 …
449 ]
450 ]
450 }
451 }
451 error : null
452 error : null
452
453
453
454
454 get_users_groups
455 get_users_groups
455 ----------------
456 ----------------
456
457
457 Lists all existing users groups. This command can be executed only using
458 Lists all existing users groups. This command can be executed only using
458 api_key belonging to user with admin rights.
459 api_key belonging to user with admin rights.
459
460
460
461
461 INPUT::
462 INPUT::
462
463
463 id : <id_for_response>
464 id : <id_for_response>
464 api_key : "<api_key>"
465 api_key : "<api_key>"
465 method : "get_users_groups"
466 method : "get_users_groups"
466 args : { }
467 args : { }
467
468
468 OUTPUT::
469 OUTPUT::
469
470
470 id : <id_given_in_input>
471 id : <id_given_in_input>
471 result : [
472 result : [
472 {
473 {
473 "users_group_id" : "<id>",
474 "users_group_id" : "<id>",
474 "group_name" : "<groupname>",
475 "group_name" : "<groupname>",
475 "active": "<bool>",
476 "active": "<bool>",
476 },
477 },
477 …
478 …
478 ]
479 ]
479 error : null
480 error : null
480
481
481
482
482 create_users_group
483 create_users_group
483 ------------------
484 ------------------
484
485
485 Creates new users group. This command can be executed only using api_key
486 Creates new users group. This command can be executed only using api_key
486 belonging to user with admin rights
487 belonging to user with admin rights
487
488
488
489
489 INPUT::
490 INPUT::
490
491
491 id : <id_for_response>
492 id : <id_for_response>
492 api_key : "<api_key>"
493 api_key : "<api_key>"
493 method : "create_users_group"
494 method : "create_users_group"
494 args: {
495 args: {
495 "group_name": "<groupname>",
496 "group_name": "<groupname>",
496 "active":"<bool> = Optional(True)"
497 "active":"<bool> = Optional(True)"
497 }
498 }
498
499
499 OUTPUT::
500 OUTPUT::
500
501
501 id : <id_given_in_input>
502 id : <id_given_in_input>
502 result: {
503 result: {
503 "msg": "created new users group `<groupname>`",
504 "msg": "created new users group `<groupname>`",
504 "users_group": {
505 "users_group": {
505 "users_group_id" : "<id>",
506 "users_group_id" : "<id>",
506 "group_name" : "<groupname>",
507 "group_name" : "<groupname>",
507 "active": "<bool>",
508 "active": "<bool>",
508 },
509 },
509 }
510 }
510 error: null
511 error: null
511
512
512
513
513 add_user_to_users_group
514 add_user_to_users_group
514 -----------------------
515 -----------------------
515
516
516 Adds a user to a users group. If user exists in that group success will be
517 Adds a user to a users group. If user exists in that group success will be
517 `false`. This command can be executed only using api_key
518 `false`. This command can be executed only using api_key
518 belonging to user with admin rights
519 belonging to user with admin rights
519
520
520
521
521 INPUT::
522 INPUT::
522
523
523 id : <id_for_response>
524 id : <id_for_response>
524 api_key : "<api_key>"
525 api_key : "<api_key>"
525 method : "add_user_users_group"
526 method : "add_user_users_group"
526 args: {
527 args: {
527 "usersgroupid" : "<users group id or name>",
528 "usersgroupid" : "<users group id or name>",
528 "userid" : "<user_id or username>",
529 "userid" : "<user_id or username>",
529 }
530 }
530
531
531 OUTPUT::
532 OUTPUT::
532
533
533 id : <id_given_in_input>
534 id : <id_given_in_input>
534 result: {
535 result: {
535 "success": True|False # depends on if member is in group
536 "success": True|False # depends on if member is in group
536 "msg": "added member `<username>` to users group `<groupname>` |
537 "msg": "added member `<username>` to users group `<groupname>` |
537 User is already in that group"
538 User is already in that group"
538 }
539 }
539 error: null
540 error: null
540
541
541
542
542 remove_user_from_users_group
543 remove_user_from_users_group
543 ----------------------------
544 ----------------------------
544
545
545 Removes a user from a users group. If user is not in given group success will
546 Removes a user from a users group. If user is not in given group success will
546 be `false`. This command can be executed only
547 be `false`. This command can be executed only
547 using api_key belonging to user with admin rights
548 using api_key belonging to user with admin rights
548
549
549
550
550 INPUT::
551 INPUT::
551
552
552 id : <id_for_response>
553 id : <id_for_response>
553 api_key : "<api_key>"
554 api_key : "<api_key>"
554 method : "remove_user_from_users_group"
555 method : "remove_user_from_users_group"
555 args: {
556 args: {
556 "usersgroupid" : "<users group id or name>",
557 "usersgroupid" : "<users group id or name>",
557 "userid" : "<user_id or username>",
558 "userid" : "<user_id or username>",
558 }
559 }
559
560
560 OUTPUT::
561 OUTPUT::
561
562
562 id : <id_given_in_input>
563 id : <id_given_in_input>
563 result: {
564 result: {
564 "success": True|False, # depends on if member is in group
565 "success": True|False, # depends on if member is in group
565 "msg": "removed member <username> from users group <groupname> |
566 "msg": "removed member <username> from users group <groupname> |
566 User wasn't in group"
567 User wasn't in group"
567 }
568 }
568 error: null
569 error: null
569
570
570
571
571 get_repo
572 get_repo
572 --------
573 --------
573
574
574 Gets an existing repository by it's name or repository_id. Members will return
575 Gets an existing repository by it's name or repository_id. Members will return
575 either users_group or user associated to that repository. This command can
576 either users_group or user associated to that repository. This command can
576 be executed only using api_key belonging to user with admin rights.
577 be executed only using api_key belonging to user with admin rights.
577
578
578
579
579 INPUT::
580 INPUT::
580
581
581 id : <id_for_response>
582 id : <id_for_response>
582 api_key : "<api_key>"
583 api_key : "<api_key>"
583 method : "get_repo"
584 method : "get_repo"
584 args: {
585 args: {
585 "repoid" : "<reponame or repo_id>"
586 "repoid" : "<reponame or repo_id>"
586 }
587 }
587
588
588 OUTPUT::
589 OUTPUT::
589
590
590 id : <id_given_in_input>
591 id : <id_given_in_input>
591 result: None if repository does not exist or
592 result: None if repository does not exist or
592 {
593 {
593 "repo_id" : "<repo_id>",
594 "repo_id" : "<repo_id>",
594 "repo_name" : "<reponame>"
595 "repo_name" : "<reponame>"
595 "repo_type" : "<repo_type>",
596 "repo_type" : "<repo_type>",
596 "clone_uri" : "<clone_uri>",
597 "clone_uri" : "<clone_uri>",
597 "enable_downloads": "<bool>",
598 "enable_downloads": "<bool>",
598 "enable_locking": "<bool>",
599 "enable_locking": "<bool>",
599 "enable_statistics": "<bool>",
600 "enable_statistics": "<bool>",
600 "private": "<bool>",
601 "private": "<bool>",
601 "created_on" : "<datetimecreated>",
602 "created_on" : "<datetimecreated>",
602 "description" : "<description>",
603 "description" : "<description>",
603 "landing_rev": "<landing_rev>",
604 "landing_rev": "<landing_rev>",
604 "owner": "<repo_owner>",
605 "owner": "<repo_owner>",
605 "fork_of": "<name_of_fork_parent>",
606 "fork_of": "<name_of_fork_parent>",
606 "members" : [
607 "members" : [
607 {
608 {
608 "type": "user",
609 "type": "user",
609 "user_id" : "<user_id>",
610 "user_id" : "<user_id>",
610 "username" : "<username>",
611 "username" : "<username>",
611 "firstname": "<firstname>",
612 "firstname": "<firstname>",
612 "lastname" : "<lastname>",
613 "lastname" : "<lastname>",
613 "email" : "<email>",
614 "email" : "<email>",
614 "emails": "<list_of_all_additional_emails>",
615 "emails": "<list_of_all_additional_emails>",
615 "active" : "<bool>",
616 "active" : "<bool>",
616 "admin" :Β  "<bool>",
617 "admin" :Β  "<bool>",
617 "ldap_dn" : "<ldap_dn>",
618 "ldap_dn" : "<ldap_dn>",
618 "last_login": "<last_login>",
619 "last_login": "<last_login>",
619 "permission" : "repository.(read|write|admin)"
620 "permission" : "repository.(read|write|admin)"
620 },
621 },
621 …
622 …
622 {
623 {
623 "type": "users_group",
624 "type": "users_group",
624 "id" : "<usersgroupid>",
625 "id" : "<usersgroupid>",
625 "name" : "<usersgroupname>",
626 "name" : "<usersgroupname>",
626 "active": "<bool>",
627 "active": "<bool>",
627 "permission" : "repository.(read|write|admin)"
628 "permission" : "repository.(read|write|admin)"
628 },
629 },
629 …
630 …
630 ]
631 ]
631 }
632 }
632 error: null
633 error: null
633
634
634
635
635 get_repos
636 get_repos
636 ---------
637 ---------
637
638
638 Lists all existing repositories. This command can be executed only using api_key
639 Lists all existing repositories. This command can be executed only using api_key
639 belonging to user with admin rights
640 belonging to user with admin rights
640
641
641
642
642 INPUT::
643 INPUT::
643
644
644 id : <id_for_response>
645 id : <id_for_response>
645 api_key : "<api_key>"
646 api_key : "<api_key>"
646 method : "get_repos"
647 method : "get_repos"
647 args: { }
648 args: { }
648
649
649 OUTPUT::
650 OUTPUT::
650
651
651 id : <id_given_in_input>
652 id : <id_given_in_input>
652 result: [
653 result: [
653 {
654 {
654 "repo_id" : "<repo_id>",
655 "repo_id" : "<repo_id>",
655 "repo_name" : "<reponame>"
656 "repo_name" : "<reponame>"
656 "repo_type" : "<repo_type>",
657 "repo_type" : "<repo_type>",
657 "clone_uri" : "<clone_uri>",
658 "clone_uri" : "<clone_uri>",
658 "private": : "<bool>",
659 "private": : "<bool>",
659 "created_on" : "<datetimecreated>",
660 "created_on" : "<datetimecreated>",
660 "description" : "<description>",
661 "description" : "<description>",
661 "landing_rev": "<landing_rev>",
662 "landing_rev": "<landing_rev>",
662 "owner": "<repo_owner>",
663 "owner": "<repo_owner>",
663 "fork_of": "<name_of_fork_parent>",
664 "fork_of": "<name_of_fork_parent>",
664 "enable_downloads": "<bool>",
665 "enable_downloads": "<bool>",
665 "enable_locking": "<bool>",
666 "enable_locking": "<bool>",
666 "enable_statistics": "<bool>",
667 "enable_statistics": "<bool>",
667 },
668 },
668 …
669 …
669 ]
670 ]
670 error: null
671 error: null
671
672
672
673
673 get_repo_nodes
674 get_repo_nodes
674 --------------
675 --------------
675
676
676 returns a list of nodes and it's children in a flat list for a given path
677 returns a list of nodes and it's children in a flat list for a given path
677 at given revision. It's possible to specify ret_type to show only `files` or
678 at given revision. It's possible to specify ret_type to show only `files` or
678 `dirs`. This command can be executed only using api_key belonging to user
679 `dirs`. This command can be executed only using api_key belonging to user
679 with admin rights
680 with admin rights
680
681
681
682
682 INPUT::
683 INPUT::
683
684
684 id : <id_for_response>
685 id : <id_for_response>
685 api_key : "<api_key>"
686 api_key : "<api_key>"
686 method : "get_repo_nodes"
687 method : "get_repo_nodes"
687 args: {
688 args: {
688 "repoid" : "<reponame or repo_id>"
689 "repoid" : "<reponame or repo_id>"
689 "revision" : "<revision>",
690 "revision" : "<revision>",
690 "root_path" : "<root_path>",
691 "root_path" : "<root_path>",
691 "ret_type" : "<ret_type> = Optional('all')"
692 "ret_type" : "<ret_type> = Optional('all')"
692 }
693 }
693
694
694 OUTPUT::
695 OUTPUT::
695
696
696 id : <id_given_in_input>
697 id : <id_given_in_input>
697 result: [
698 result: [
698 {
699 {
699 "name" : "<name>"
700 "name" : "<name>"
700 "type" : "<type>",
701 "type" : "<type>",
701 },
702 },
702 …
703 …
703 ]
704 ]
704 error: null
705 error: null
705
706
706
707
707 create_repo
708 create_repo
708 -----------
709 -----------
709
710
710 Creates a repository. This command can be executed only using api_key
711 Creates a repository. This command can be executed only using api_key
711 belonging to user with admin rights.
712 belonging to user with admin rights.
712 If repository name contains "/", all needed repository groups will be created.
713 If repository name contains "/", all needed repository groups will be created.
713 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
714 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
714 and create "baz" repository with "bar" as group.
715 and create "baz" repository with "bar" as group.
715
716
716
717
717 INPUT::
718 INPUT::
718
719
719 id : <id_for_response>
720 id : <id_for_response>
720 api_key : "<api_key>"
721 api_key : "<api_key>"
721 method : "create_repo"
722 method : "create_repo"
722 args: {
723 args: {
723 "repo_name" : "<reponame>",
724 "repo_name" : "<reponame>",
724 "owner" : "<onwer_name_or_id>",
725 "owner" : "<onwer_name_or_id>",
725 "repo_type" : "<repo_type> = Optional('hg')",
726 "repo_type" : "<repo_type> = Optional('hg')",
726 "description" : "<description> = Optional('')",
727 "description" : "<description> = Optional('')",
727 "private" : "<bool> = Optional(False)",
728 "private" : "<bool> = Optional(False)",
728 "clone_uri" : "<clone_uri> = Optional(None)",
729 "clone_uri" : "<clone_uri> = Optional(None)",
729 "landing_rev" : "<landing_rev> = Optional('tip')",
730 "landing_rev" : "<landing_rev> = Optional('tip')",
730 "enable_downloads": "<bool> = Optional(False)",
731 "enable_downloads": "<bool> = Optional(False)",
731 "enable_locking": "<bool> = Optional(False)",
732 "enable_locking": "<bool> = Optional(False)",
732 "enable_statistics": "<bool> = Optional(False)",
733 "enable_statistics": "<bool> = Optional(False)",
733 }
734 }
734
735
735 OUTPUT::
736 OUTPUT::
736
737
737 id : <id_given_in_input>
738 id : <id_given_in_input>
738 result: {
739 result: {
739 "msg": "Created new repository `<reponame>`",
740 "msg": "Created new repository `<reponame>`",
740 "repo": {
741 "repo": {
741 "repo_id" : "<repo_id>",
742 "repo_id" : "<repo_id>",
742 "repo_name" : "<reponame>"
743 "repo_name" : "<reponame>"
743 "repo_type" : "<repo_type>",
744 "repo_type" : "<repo_type>",
744 "clone_uri" : "<clone_uri>",
745 "clone_uri" : "<clone_uri>",
745 "private": : "<bool>",
746 "private": : "<bool>",
746 "created_on" : "<datetimecreated>",
747 "created_on" : "<datetimecreated>",
747 "description" : "<description>",
748 "description" : "<description>",
748 "landing_rev": "<landing_rev>",
749 "landing_rev": "<landing_rev>",
749 "owner": "<username or user_id>",
750 "owner": "<username or user_id>",
750 "fork_of": "<name_of_fork_parent>",
751 "fork_of": "<name_of_fork_parent>",
751 "enable_downloads": "<bool>",
752 "enable_downloads": "<bool>",
752 "enable_locking": "<bool>",
753 "enable_locking": "<bool>",
753 "enable_statistics": "<bool>",
754 "enable_statistics": "<bool>",
754 },
755 },
755 }
756 }
756 error: null
757 error: null
757
758
758
759
759 fork_repo
760 fork_repo
760 ---------
761 ---------
761
762
762 Creates a fork of given repo. This command can be executed only using api_key
763 Creates a fork of given repo. This command can be executed only using api_key
763 belonging to user with admin rights. In case of using celery this will
764 belonging to user with admin rights. In case of using celery this will
764 immidiatelly return success message, while fork is going to be created
765 immidiatelly return success message, while fork is going to be created
765 asynchronous
766 asynchronous
766
767
767
768
768 INPUT::
769 INPUT::
769
770
770 id : <id_for_response>
771 id : <id_for_response>
771 api_key : "<api_key>"
772 api_key : "<api_key>"
772 method : "fork_repo"
773 method : "fork_repo"
773 args: {
774 args: {
774 "repoid" : "<reponame or repo_id>",
775 "repoid" : "<reponame or repo_id>",
775 "fork_name": "<forkname>",
776 "fork_name": "<forkname>",
776 "owner": "<username or user_id>",
777 "owner": "<username or user_id>",
777 "description": "<description>",
778 "description": "<description>",
778 "copy_permissions": "<bool>",
779 "copy_permissions": "<bool>",
779 "private": "<bool>",
780 "private": "<bool>",
780 "landing_rev": "<landing_rev>"
781 "landing_rev": "<landing_rev>"
781
782
782 }
783 }
783
784
784 OUTPUT::
785 OUTPUT::
785
786
786 id : <id_given_in_input>
787 id : <id_given_in_input>
787 result: {
788 result: {
788 "msg": "Created fork of `<reponame>` as `<forkname>`",
789 "msg": "Created fork of `<reponame>` as `<forkname>`",
789 "success": true
790 "success": true
790 }
791 }
791 error: null
792 error: null
792
793
793
794
794 delete_repo
795 delete_repo
795 -----------
796 -----------
796
797
797 Deletes a repository. This command can be executed only using api_key
798 Deletes a repository. This command can be executed only using api_key
798 belonging to user with admin rights.
799 belonging to user with admin rights.
799
800
800
801
801 INPUT::
802 INPUT::
802
803
803 id : <id_for_response>
804 id : <id_for_response>
804 api_key : "<api_key>"
805 api_key : "<api_key>"
805 method : "delete_repo"
806 method : "delete_repo"
806 args: {
807 args: {
807 "repoid" : "<reponame or repo_id>"
808 "repoid" : "<reponame or repo_id>"
808 }
809 }
809
810
810 OUTPUT::
811 OUTPUT::
811
812
812 id : <id_given_in_input>
813 id : <id_given_in_input>
813 result: {
814 result: {
814 "msg": "Deleted repository `<reponame>`",
815 "msg": "Deleted repository `<reponame>`",
815 "success": true
816 "success": true
816 }
817 }
817 error: null
818 error: null
818
819
819
820
820 grant_user_permission
821 grant_user_permission
821 ---------------------
822 ---------------------
822
823
823 Grant permission for user on given repository, or update existing one
824 Grant permission for user on given repository, or update existing one
824 if found. This command can be executed only using api_key belonging to user
825 if found. This command can be executed only using api_key belonging to user
825 with admin rights.
826 with admin rights.
826
827
827
828
828 INPUT::
829 INPUT::
829
830
830 id : <id_for_response>
831 id : <id_for_response>
831 api_key : "<api_key>"
832 api_key : "<api_key>"
832 method : "grant_user_permission"
833 method : "grant_user_permission"
833 args: {
834 args: {
834 "repoid" : "<reponame or repo_id>"
835 "repoid" : "<reponame or repo_id>"
835 "userid" : "<username or user_id>"
836 "userid" : "<username or user_id>"
836 "perm" : "(repository.(none|read|write|admin))",
837 "perm" : "(repository.(none|read|write|admin))",
837 }
838 }
838
839
839 OUTPUT::
840 OUTPUT::
840
841
841 id : <id_given_in_input>
842 id : <id_given_in_input>
842 result: {
843 result: {
843 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
844 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
844 "success": true
845 "success": true
845 }
846 }
846 error: null
847 error: null
847
848
848
849
849 revoke_user_permission
850 revoke_user_permission
850 ----------------------
851 ----------------------
851
852
852 Revoke permission for user on given repository. This command can be executed
853 Revoke permission for user on given repository. This command can be executed
853 only using api_key belonging to user with admin rights.
854 only using api_key belonging to user with admin rights.
854
855
855
856
856 INPUT::
857 INPUT::
857
858
858 id : <id_for_response>
859 id : <id_for_response>
859 api_key : "<api_key>"
860 api_key : "<api_key>"
860 method : "revoke_user_permission"
861 method : "revoke_user_permission"
861 args: {
862 args: {
862 "repoid" : "<reponame or repo_id>"
863 "repoid" : "<reponame or repo_id>"
863 "userid" : "<username or user_id>"
864 "userid" : "<username or user_id>"
864 }
865 }
865
866
866 OUTPUT::
867 OUTPUT::
867
868
868 id : <id_given_in_input>
869 id : <id_given_in_input>
869 result: {
870 result: {
870 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
871 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
871 "success": true
872 "success": true
872 }
873 }
873 error: null
874 error: null
874
875
875
876
876 grant_users_group_permission
877 grant_users_group_permission
877 ----------------------------
878 ----------------------------
878
879
879 Grant permission for users group on given repository, or update
880 Grant permission for users group on given repository, or update
880 existing one if found. This command can be executed only using
881 existing one if found. This command can be executed only using
881 api_key belonging to user with admin rights.
882 api_key belonging to user with admin rights.
882
883
883
884
884 INPUT::
885 INPUT::
885
886
886 id : <id_for_response>
887 id : <id_for_response>
887 api_key : "<api_key>"
888 api_key : "<api_key>"
888 method : "grant_users_group_permission"
889 method : "grant_users_group_permission"
889 args: {
890 args: {
890 "repoid" : "<reponame or repo_id>"
891 "repoid" : "<reponame or repo_id>"
891 "usersgroupid" : "<users group id or name>"
892 "usersgroupid" : "<users group id or name>"
892 "perm" : "(repository.(none|read|write|admin))",
893 "perm" : "(repository.(none|read|write|admin))",
893 }
894 }
894
895
895 OUTPUT::
896 OUTPUT::
896
897
897 id : <id_given_in_input>
898 id : <id_given_in_input>
898 result: {
899 result: {
899 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
900 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
900 "success": true
901 "success": true
901 }
902 }
902 error: null
903 error: null
903
904
904
905
905 revoke_users_group_permission
906 revoke_users_group_permission
906 -----------------------------
907 -----------------------------
907
908
908 Revoke permission for users group on given repository.This command can be
909 Revoke permission for users group on given repository.This command can be
909 executed only using api_key belonging to user with admin rights.
910 executed only using api_key belonging to user with admin rights.
910
911
911 INPUT::
912 INPUT::
912
913
913 id : <id_for_response>
914 id : <id_for_response>
914 api_key : "<api_key>"
915 api_key : "<api_key>"
915 method : "revoke_users_group_permission"
916 method : "revoke_users_group_permission"
916 args: {
917 args: {
917 "repoid" : "<reponame or repo_id>"
918 "repoid" : "<reponame or repo_id>"
918 "usersgroupid" : "<users group id or name>"
919 "usersgroupid" : "<users group id or name>"
919 }
920 }
920
921
921 OUTPUT::
922 OUTPUT::
922
923
923 id : <id_given_in_input>
924 id : <id_given_in_input>
924 result: {
925 result: {
925 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
926 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
926 "success": true
927 "success": true
927 }
928 }
928 error: null No newline at end of file
929 error: null
@@ -1,844 +1,877 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 API controller for RhodeCode
6 API controller for RhodeCode
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import traceback
28 import traceback
29 import logging
29 import logging
30 from pylons.controllers.util import abort
30
31
31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 from rhodecode.lib.auth import HasPermissionAllDecorator, \
33 from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
33 HasPermissionAnyDecorator, PasswordGenerator, AuthUser
34 HasPermissionAllDecorator, HasPermissionAnyDecorator, \
35 HasPermissionAnyApi, HasRepoPermissionAnyApi
34 from rhodecode.lib.utils import map_groups, repo2db_mapper
36 from rhodecode.lib.utils import map_groups, repo2db_mapper
35 from rhodecode.model.meta import Session
37 from rhodecode.model.meta import Session
36 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
39 from rhodecode.model.users_group import UsersGroupModel
41 from rhodecode.model.users_group import UsersGroupModel
40 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
43 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
42
44
43 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
44
46
45
47
48 class OptionalAttr(object):
49 """
50 Special Optional Option that defines other attribute
51 """
52 def __init__(self, attr_name):
53 self.attr_name = attr_name
54
55 def __repr__(self):
56 return '<OptionalAttr:%s>' % self.attr_name
57
58 def __call__(self):
59 return self
60 #alias
61 OAttr = OptionalAttr
62
63
46 class Optional(object):
64 class Optional(object):
47 """
65 """
48 Defines an optional parameter::
66 Defines an optional parameter::
49
67
50 param = param.getval() if isinstance(param, Optional) else param
68 param = param.getval() if isinstance(param, Optional) else param
51 param = param() if isinstance(param, Optional) else param
69 param = param() if isinstance(param, Optional) else param
52
70
53 is equivalent of::
71 is equivalent of::
54
72
55 param = Optional.extract(param)
73 param = Optional.extract(param)
56
74
57 """
75 """
58 def __init__(self, type_):
76 def __init__(self, type_):
59 self.type_ = type_
77 self.type_ = type_
60
78
61 def __repr__(self):
79 def __repr__(self):
62 return '<Optional:%s>' % self.type_.__repr__()
80 return '<Optional:%s>' % self.type_.__repr__()
63
81
64 def __call__(self):
82 def __call__(self):
65 return self.getval()
83 return self.getval()
66
84
67 def getval(self):
85 def getval(self):
68 """
86 """
69 returns value from this Optional instance
87 returns value from this Optional instance
70 """
88 """
71 return self.type_
89 return self.type_
72
90
73 @classmethod
91 @classmethod
74 def extract(cls, val):
92 def extract(cls, val):
75 if isinstance(val, cls):
93 if isinstance(val, cls):
76 return val.getval()
94 return val.getval()
77 return val
95 return val
78
96
79
97
80 def get_user_or_error(userid):
98 def get_user_or_error(userid):
81 """
99 """
82 Get user by id or name or return JsonRPCError if not found
100 Get user by id or name or return JsonRPCError if not found
83
101
84 :param userid:
102 :param userid:
85 """
103 """
86 user = UserModel().get_user(userid)
104 user = UserModel().get_user(userid)
87 if user is None:
105 if user is None:
88 raise JSONRPCError("user `%s` does not exist" % userid)
106 raise JSONRPCError("user `%s` does not exist" % userid)
89 return user
107 return user
90
108
91
109
92 def get_repo_or_error(repoid):
110 def get_repo_or_error(repoid):
93 """
111 """
94 Get repo by id or name or return JsonRPCError if not found
112 Get repo by id or name or return JsonRPCError if not found
95
113
96 :param userid:
114 :param userid:
97 """
115 """
98 repo = RepoModel().get_repo(repoid)
116 repo = RepoModel().get_repo(repoid)
99 if repo is None:
117 if repo is None:
100 raise JSONRPCError('repository `%s` does not exist' % (repoid))
118 raise JSONRPCError('repository `%s` does not exist' % (repoid))
101 return repo
119 return repo
102
120
103
121
104 def get_users_group_or_error(usersgroupid):
122 def get_users_group_or_error(usersgroupid):
105 """
123 """
106 Get users group by id or name or return JsonRPCError if not found
124 Get users group by id or name or return JsonRPCError if not found
107
125
108 :param userid:
126 :param userid:
109 """
127 """
110 users_group = UsersGroupModel().get_group(usersgroupid)
128 users_group = UsersGroupModel().get_group(usersgroupid)
111 if users_group is None:
129 if users_group is None:
112 raise JSONRPCError('users group `%s` does not exist' % usersgroupid)
130 raise JSONRPCError('users group `%s` does not exist' % usersgroupid)
113 return users_group
131 return users_group
114
132
115
133
116 def get_perm_or_error(permid):
134 def get_perm_or_error(permid):
117 """
135 """
118 Get permission by id or name or return JsonRPCError if not found
136 Get permission by id or name or return JsonRPCError if not found
119
137
120 :param userid:
138 :param userid:
121 """
139 """
122 perm = PermissionModel().get_permission_by_name(permid)
140 perm = PermissionModel().get_permission_by_name(permid)
123 if perm is None:
141 if perm is None:
124 raise JSONRPCError('permission `%s` does not exist' % (permid))
142 raise JSONRPCError('permission `%s` does not exist' % (permid))
125 return perm
143 return perm
126
144
127
145
128 class ApiController(JSONRPCController):
146 class ApiController(JSONRPCController):
129 """
147 """
130 API Controller
148 API Controller
131
149
132
150
133 Each method needs to have USER as argument this is then based on given
151 Each method needs to have USER as argument this is then based on given
134 API_KEY propagated as instance of user object
152 API_KEY propagated as instance of user object
135
153
136 Preferably this should be first argument also
154 Preferably this should be first argument also
137
155
138
156
139 Each function should also **raise** JSONRPCError for any
157 Each function should also **raise** JSONRPCError for any
140 errors that happens
158 errors that happens
141
159
142 """
160 """
143
161
144 @HasPermissionAllDecorator('hg.admin')
162 @HasPermissionAllDecorator('hg.admin')
145 def pull(self, apiuser, repoid):
163 def pull(self, apiuser, repoid):
146 """
164 """
147 Dispatch pull action on given repo
165 Dispatch pull action on given repo
148
166
149 :param apiuser:
167 :param apiuser:
150 :param repoid:
168 :param repoid:
151 """
169 """
152
170
153 repo = get_repo_or_error(repoid)
171 repo = get_repo_or_error(repoid)
154
172
155 try:
173 try:
156 ScmModel().pull_changes(repo.repo_name,
174 ScmModel().pull_changes(repo.repo_name,
157 self.rhodecode_user.username)
175 self.rhodecode_user.username)
158 return 'Pulled from `%s`' % repo.repo_name
176 return 'Pulled from `%s`' % repo.repo_name
159 except Exception:
177 except Exception:
160 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
161 raise JSONRPCError(
179 raise JSONRPCError(
162 'Unable to pull changes from `%s`' % repo.repo_name
180 'Unable to pull changes from `%s`' % repo.repo_name
163 )
181 )
164
182
165 @HasPermissionAllDecorator('hg.admin')
183 @HasPermissionAllDecorator('hg.admin')
166 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
184 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
167 """
185 """
168 Dispatch rescan repositories action. If remove_obsolete is set
186 Dispatch rescan repositories action. If remove_obsolete is set
169 than also delete repos that are in database but not in the filesystem.
187 than also delete repos that are in database but not in the filesystem.
170 aka "clean zombies"
188 aka "clean zombies"
171
189
172 :param apiuser:
190 :param apiuser:
173 :param remove_obsolete:
191 :param remove_obsolete:
174 """
192 """
175
193
176 try:
194 try:
177 rm_obsolete = Optional.extract(remove_obsolete)
195 rm_obsolete = Optional.extract(remove_obsolete)
178 added, removed = repo2db_mapper(ScmModel().repo_scan(),
196 added, removed = repo2db_mapper(ScmModel().repo_scan(),
179 remove_obsolete=rm_obsolete)
197 remove_obsolete=rm_obsolete)
180 return {'added': added, 'removed': removed}
198 return {'added': added, 'removed': removed}
181 except Exception:
199 except Exception:
182 log.error(traceback.format_exc())
200 log.error(traceback.format_exc())
183 raise JSONRPCError(
201 raise JSONRPCError(
184 'Error occurred during rescan repositories action'
202 'Error occurred during rescan repositories action'
185 )
203 )
186
204
187 @HasPermissionAllDecorator('hg.admin')
205 def lock(self, apiuser, repoid, locked, userid=Optional(OAttr('apiuser'))):
188 def lock(self, apiuser, repoid, userid, locked):
189 """
206 """
190 Set locking state on particular repository by given user
207 Set locking state on particular repository by given user, if
208 this command is runned by non-admin account userid is set to user
209 who is calling this method
191
210
192 :param apiuser:
211 :param apiuser:
193 :param repoid:
212 :param repoid:
194 :param userid:
213 :param userid:
195 :param locked:
214 :param locked:
196 """
215 """
197 repo = get_repo_or_error(repoid)
216 repo = get_repo_or_error(repoid)
217 if HasPermissionAnyApi('hg.admin')(user=apiuser):
218 pass
219 elif HasRepoPermissionAnyApi('repository.admin',
220 'repository.write')(user=apiuser,
221 repo_name=repo.repo_name):
222 #make sure normal user does not pass userid, he is not allowed to do that
223 if not isinstance(userid, Optional):
224 raise JSONRPCError(
225 'Only RhodeCode admin can specify `userid` params'
226 )
227 else:
228 return abort(403)
229 if isinstance(userid, Optional):
230 userid = apiuser.user_id
198 user = get_user_or_error(userid)
231 user = get_user_or_error(userid)
199 locked = bool(locked)
232 locked = bool(locked)
200 try:
233 try:
201 if locked:
234 if locked:
202 Repository.lock(repo, user.user_id)
235 Repository.lock(repo, user.user_id)
203 else:
236 else:
204 Repository.unlock(repo)
237 Repository.unlock(repo)
205
238
206 return ('User `%s` set lock state for repo `%s` to `%s`'
239 return ('User `%s` set lock state for repo `%s` to `%s`'
207 % (user.username, repo.repo_name, locked))
240 % (user.username, repo.repo_name, locked))
208 except Exception:
241 except Exception:
209 log.error(traceback.format_exc())
242 log.error(traceback.format_exc())
210 raise JSONRPCError(
243 raise JSONRPCError(
211 'Error occurred locking repository `%s`' % repo.repo_name
244 'Error occurred locking repository `%s`' % repo.repo_name
212 )
245 )
213
246
214 @HasPermissionAllDecorator('hg.admin')
247 @HasPermissionAllDecorator('hg.admin')
215 def show_ip(self, apiuser, userid):
248 def show_ip(self, apiuser, userid):
216 """
249 """
217 Shows IP address as seen from RhodeCode server, together with all
250 Shows IP address as seen from RhodeCode server, together with all
218 defined IP addresses for given user
251 defined IP addresses for given user
219
252
220 :param apiuser:
253 :param apiuser:
221 :param userid:
254 :param userid:
222 """
255 """
223 user = get_user_or_error(userid)
256 user = get_user_or_error(userid)
224 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
257 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
225 return dict(
258 return dict(
226 ip_addr_server=self.ip_addr,
259 ip_addr_server=self.ip_addr,
227 user_ips=ips
260 user_ips=ips
228 )
261 )
229
262
230 @HasPermissionAllDecorator('hg.admin')
263 @HasPermissionAllDecorator('hg.admin')
231 def get_user(self, apiuser, userid):
264 def get_user(self, apiuser, userid):
232 """"
265 """"
233 Get a user by username
266 Get a user by username
234
267
235 :param apiuser:
268 :param apiuser:
236 :param userid:
269 :param userid:
237 """
270 """
238
271
239 user = get_user_or_error(userid)
272 user = get_user_or_error(userid)
240 data = user.get_api_data()
273 data = user.get_api_data()
241 data['permissions'] = AuthUser(user_id=user.user_id).permissions
274 data['permissions'] = AuthUser(user_id=user.user_id).permissions
242 return data
275 return data
243
276
244 @HasPermissionAllDecorator('hg.admin')
277 @HasPermissionAllDecorator('hg.admin')
245 def get_users(self, apiuser):
278 def get_users(self, apiuser):
246 """"
279 """"
247 Get all users
280 Get all users
248
281
249 :param apiuser:
282 :param apiuser:
250 """
283 """
251
284
252 result = []
285 result = []
253 for user in UserModel().get_all():
286 for user in UserModel().get_all():
254 result.append(user.get_api_data())
287 result.append(user.get_api_data())
255 return result
288 return result
256
289
257 @HasPermissionAllDecorator('hg.admin')
290 @HasPermissionAllDecorator('hg.admin')
258 def create_user(self, apiuser, username, email, password,
291 def create_user(self, apiuser, username, email, password,
259 firstname=Optional(None), lastname=Optional(None),
292 firstname=Optional(None), lastname=Optional(None),
260 active=Optional(True), admin=Optional(False),
293 active=Optional(True), admin=Optional(False),
261 ldap_dn=Optional(None)):
294 ldap_dn=Optional(None)):
262 """
295 """
263 Create new user
296 Create new user
264
297
265 :param apiuser:
298 :param apiuser:
266 :param username:
299 :param username:
267 :param email:
300 :param email:
268 :param password:
301 :param password:
269 :param firstname:
302 :param firstname:
270 :param lastname:
303 :param lastname:
271 :param active:
304 :param active:
272 :param admin:
305 :param admin:
273 :param ldap_dn:
306 :param ldap_dn:
274 """
307 """
275
308
276 if UserModel().get_by_username(username):
309 if UserModel().get_by_username(username):
277 raise JSONRPCError("user `%s` already exist" % username)
310 raise JSONRPCError("user `%s` already exist" % username)
278
311
279 if UserModel().get_by_email(email, case_insensitive=True):
312 if UserModel().get_by_email(email, case_insensitive=True):
280 raise JSONRPCError("email `%s` already exist" % email)
313 raise JSONRPCError("email `%s` already exist" % email)
281
314
282 if Optional.extract(ldap_dn):
315 if Optional.extract(ldap_dn):
283 # generate temporary password if ldap_dn
316 # generate temporary password if ldap_dn
284 password = PasswordGenerator().gen_password(length=8)
317 password = PasswordGenerator().gen_password(length=8)
285
318
286 try:
319 try:
287 user = UserModel().create_or_update(
320 user = UserModel().create_or_update(
288 username=Optional.extract(username),
321 username=Optional.extract(username),
289 password=Optional.extract(password),
322 password=Optional.extract(password),
290 email=Optional.extract(email),
323 email=Optional.extract(email),
291 firstname=Optional.extract(firstname),
324 firstname=Optional.extract(firstname),
292 lastname=Optional.extract(lastname),
325 lastname=Optional.extract(lastname),
293 active=Optional.extract(active),
326 active=Optional.extract(active),
294 admin=Optional.extract(admin),
327 admin=Optional.extract(admin),
295 ldap_dn=Optional.extract(ldap_dn)
328 ldap_dn=Optional.extract(ldap_dn)
296 )
329 )
297 Session().commit()
330 Session().commit()
298 return dict(
331 return dict(
299 msg='created new user `%s`' % username,
332 msg='created new user `%s`' % username,
300 user=user.get_api_data()
333 user=user.get_api_data()
301 )
334 )
302 except Exception:
335 except Exception:
303 log.error(traceback.format_exc())
336 log.error(traceback.format_exc())
304 raise JSONRPCError('failed to create user `%s`' % username)
337 raise JSONRPCError('failed to create user `%s`' % username)
305
338
306 @HasPermissionAllDecorator('hg.admin')
339 @HasPermissionAllDecorator('hg.admin')
307 def update_user(self, apiuser, userid, username=Optional(None),
340 def update_user(self, apiuser, userid, username=Optional(None),
308 email=Optional(None), firstname=Optional(None),
341 email=Optional(None), firstname=Optional(None),
309 lastname=Optional(None), active=Optional(None),
342 lastname=Optional(None), active=Optional(None),
310 admin=Optional(None), ldap_dn=Optional(None),
343 admin=Optional(None), ldap_dn=Optional(None),
311 password=Optional(None)):
344 password=Optional(None)):
312 """
345 """
313 Updates given user
346 Updates given user
314
347
315 :param apiuser:
348 :param apiuser:
316 :param userid:
349 :param userid:
317 :param username:
350 :param username:
318 :param email:
351 :param email:
319 :param firstname:
352 :param firstname:
320 :param lastname:
353 :param lastname:
321 :param active:
354 :param active:
322 :param admin:
355 :param admin:
323 :param ldap_dn:
356 :param ldap_dn:
324 :param password:
357 :param password:
325 """
358 """
326
359
327 user = get_user_or_error(userid)
360 user = get_user_or_error(userid)
328
361
329 # call function and store only updated arguments
362 # call function and store only updated arguments
330 updates = {}
363 updates = {}
331
364
332 def store_update(attr, name):
365 def store_update(attr, name):
333 if not isinstance(attr, Optional):
366 if not isinstance(attr, Optional):
334 updates[name] = attr
367 updates[name] = attr
335
368
336 try:
369 try:
337
370
338 store_update(username, 'username')
371 store_update(username, 'username')
339 store_update(password, 'password')
372 store_update(password, 'password')
340 store_update(email, 'email')
373 store_update(email, 'email')
341 store_update(firstname, 'name')
374 store_update(firstname, 'name')
342 store_update(lastname, 'lastname')
375 store_update(lastname, 'lastname')
343 store_update(active, 'active')
376 store_update(active, 'active')
344 store_update(admin, 'admin')
377 store_update(admin, 'admin')
345 store_update(ldap_dn, 'ldap_dn')
378 store_update(ldap_dn, 'ldap_dn')
346
379
347 user = UserModel().update_user(user, **updates)
380 user = UserModel().update_user(user, **updates)
348 Session().commit()
381 Session().commit()
349 return dict(
382 return dict(
350 msg='updated user ID:%s %s' % (user.user_id, user.username),
383 msg='updated user ID:%s %s' % (user.user_id, user.username),
351 user=user.get_api_data()
384 user=user.get_api_data()
352 )
385 )
353 except Exception:
386 except Exception:
354 log.error(traceback.format_exc())
387 log.error(traceback.format_exc())
355 raise JSONRPCError('failed to update user `%s`' % userid)
388 raise JSONRPCError('failed to update user `%s`' % userid)
356
389
357 @HasPermissionAllDecorator('hg.admin')
390 @HasPermissionAllDecorator('hg.admin')
358 def delete_user(self, apiuser, userid):
391 def delete_user(self, apiuser, userid):
359 """"
392 """"
360 Deletes an user
393 Deletes an user
361
394
362 :param apiuser:
395 :param apiuser:
363 :param userid:
396 :param userid:
364 """
397 """
365 user = get_user_or_error(userid)
398 user = get_user_or_error(userid)
366
399
367 try:
400 try:
368 UserModel().delete(userid)
401 UserModel().delete(userid)
369 Session().commit()
402 Session().commit()
370 return dict(
403 return dict(
371 msg='deleted user ID:%s %s' % (user.user_id, user.username),
404 msg='deleted user ID:%s %s' % (user.user_id, user.username),
372 user=None
405 user=None
373 )
406 )
374 except Exception:
407 except Exception:
375 log.error(traceback.format_exc())
408 log.error(traceback.format_exc())
376 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
409 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
377 user.username))
410 user.username))
378
411
379 @HasPermissionAllDecorator('hg.admin')
412 @HasPermissionAllDecorator('hg.admin')
380 def get_users_group(self, apiuser, usersgroupid):
413 def get_users_group(self, apiuser, usersgroupid):
381 """"
414 """"
382 Get users group by name or id
415 Get users group by name or id
383
416
384 :param apiuser:
417 :param apiuser:
385 :param usersgroupid:
418 :param usersgroupid:
386 """
419 """
387 users_group = get_users_group_or_error(usersgroupid)
420 users_group = get_users_group_or_error(usersgroupid)
388
421
389 data = users_group.get_api_data()
422 data = users_group.get_api_data()
390
423
391 members = []
424 members = []
392 for user in users_group.members:
425 for user in users_group.members:
393 user = user.user
426 user = user.user
394 members.append(user.get_api_data())
427 members.append(user.get_api_data())
395 data['members'] = members
428 data['members'] = members
396 return data
429 return data
397
430
398 @HasPermissionAllDecorator('hg.admin')
431 @HasPermissionAllDecorator('hg.admin')
399 def get_users_groups(self, apiuser):
432 def get_users_groups(self, apiuser):
400 """"
433 """"
401 Get all users groups
434 Get all users groups
402
435
403 :param apiuser:
436 :param apiuser:
404 """
437 """
405
438
406 result = []
439 result = []
407 for users_group in UsersGroupModel().get_all():
440 for users_group in UsersGroupModel().get_all():
408 result.append(users_group.get_api_data())
441 result.append(users_group.get_api_data())
409 return result
442 return result
410
443
411 @HasPermissionAllDecorator('hg.admin')
444 @HasPermissionAllDecorator('hg.admin')
412 def create_users_group(self, apiuser, group_name, active=Optional(True)):
445 def create_users_group(self, apiuser, group_name, active=Optional(True)):
413 """
446 """
414 Creates an new usergroup
447 Creates an new usergroup
415
448
416 :param apiuser:
449 :param apiuser:
417 :param group_name:
450 :param group_name:
418 :param active:
451 :param active:
419 """
452 """
420
453
421 if UsersGroupModel().get_by_name(group_name):
454 if UsersGroupModel().get_by_name(group_name):
422 raise JSONRPCError("users group `%s` already exist" % group_name)
455 raise JSONRPCError("users group `%s` already exist" % group_name)
423
456
424 try:
457 try:
425 active = Optional.extract(active)
458 active = Optional.extract(active)
426 ug = UsersGroupModel().create(name=group_name, active=active)
459 ug = UsersGroupModel().create(name=group_name, active=active)
427 Session().commit()
460 Session().commit()
428 return dict(
461 return dict(
429 msg='created new users group `%s`' % group_name,
462 msg='created new users group `%s`' % group_name,
430 users_group=ug.get_api_data()
463 users_group=ug.get_api_data()
431 )
464 )
432 except Exception:
465 except Exception:
433 log.error(traceback.format_exc())
466 log.error(traceback.format_exc())
434 raise JSONRPCError('failed to create group `%s`' % group_name)
467 raise JSONRPCError('failed to create group `%s`' % group_name)
435
468
436 @HasPermissionAllDecorator('hg.admin')
469 @HasPermissionAllDecorator('hg.admin')
437 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
470 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
438 """"
471 """"
439 Add a user to a users group
472 Add a user to a users group
440
473
441 :param apiuser:
474 :param apiuser:
442 :param usersgroupid:
475 :param usersgroupid:
443 :param userid:
476 :param userid:
444 """
477 """
445 user = get_user_or_error(userid)
478 user = get_user_or_error(userid)
446 users_group = get_users_group_or_error(usersgroupid)
479 users_group = get_users_group_or_error(usersgroupid)
447
480
448 try:
481 try:
449 ugm = UsersGroupModel().add_user_to_group(users_group, user)
482 ugm = UsersGroupModel().add_user_to_group(users_group, user)
450 success = True if ugm != True else False
483 success = True if ugm != True else False
451 msg = 'added member `%s` to users group `%s`' % (
484 msg = 'added member `%s` to users group `%s`' % (
452 user.username, users_group.users_group_name
485 user.username, users_group.users_group_name
453 )
486 )
454 msg = msg if success else 'User is already in that group'
487 msg = msg if success else 'User is already in that group'
455 Session().commit()
488 Session().commit()
456
489
457 return dict(
490 return dict(
458 success=success,
491 success=success,
459 msg=msg
492 msg=msg
460 )
493 )
461 except Exception:
494 except Exception:
462 log.error(traceback.format_exc())
495 log.error(traceback.format_exc())
463 raise JSONRPCError(
496 raise JSONRPCError(
464 'failed to add member to users group `%s`' % (
497 'failed to add member to users group `%s`' % (
465 users_group.users_group_name
498 users_group.users_group_name
466 )
499 )
467 )
500 )
468
501
469 @HasPermissionAllDecorator('hg.admin')
502 @HasPermissionAllDecorator('hg.admin')
470 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
503 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
471 """
504 """
472 Remove user from a group
505 Remove user from a group
473
506
474 :param apiuser:
507 :param apiuser:
475 :param usersgroupid:
508 :param usersgroupid:
476 :param userid:
509 :param userid:
477 """
510 """
478 user = get_user_or_error(userid)
511 user = get_user_or_error(userid)
479 users_group = get_users_group_or_error(usersgroupid)
512 users_group = get_users_group_or_error(usersgroupid)
480
513
481 try:
514 try:
482 success = UsersGroupModel().remove_user_from_group(users_group,
515 success = UsersGroupModel().remove_user_from_group(users_group,
483 user)
516 user)
484 msg = 'removed member `%s` from users group `%s`' % (
517 msg = 'removed member `%s` from users group `%s`' % (
485 user.username, users_group.users_group_name
518 user.username, users_group.users_group_name
486 )
519 )
487 msg = msg if success else "User wasn't in group"
520 msg = msg if success else "User wasn't in group"
488 Session().commit()
521 Session().commit()
489 return dict(success=success, msg=msg)
522 return dict(success=success, msg=msg)
490 except Exception:
523 except Exception:
491 log.error(traceback.format_exc())
524 log.error(traceback.format_exc())
492 raise JSONRPCError(
525 raise JSONRPCError(
493 'failed to remove member from users group `%s`' % (
526 'failed to remove member from users group `%s`' % (
494 users_group.users_group_name
527 users_group.users_group_name
495 )
528 )
496 )
529 )
497
530
498 @HasPermissionAnyDecorator('hg.admin')
531 @HasPermissionAllDecorator('hg.admin')
499 def get_repo(self, apiuser, repoid):
532 def get_repo(self, apiuser, repoid):
500 """"
533 """"
501 Get repository by name
534 Get repository by name
502
535
503 :param apiuser:
536 :param apiuser:
504 :param repoid:
537 :param repoid:
505 """
538 """
506 repo = get_repo_or_error(repoid)
539 repo = get_repo_or_error(repoid)
507
540
508 members = []
541 members = []
509 for user in repo.repo_to_perm:
542 for user in repo.repo_to_perm:
510 perm = user.permission.permission_name
543 perm = user.permission.permission_name
511 user = user.user
544 user = user.user
512 user_data = user.get_api_data()
545 user_data = user.get_api_data()
513 user_data['type'] = "user"
546 user_data['type'] = "user"
514 user_data['permission'] = perm
547 user_data['permission'] = perm
515 members.append(user_data)
548 members.append(user_data)
516
549
517 for users_group in repo.users_group_to_perm:
550 for users_group in repo.users_group_to_perm:
518 perm = users_group.permission.permission_name
551 perm = users_group.permission.permission_name
519 users_group = users_group.users_group
552 users_group = users_group.users_group
520 users_group_data = users_group.get_api_data()
553 users_group_data = users_group.get_api_data()
521 users_group_data['type'] = "users_group"
554 users_group_data['type'] = "users_group"
522 users_group_data['permission'] = perm
555 users_group_data['permission'] = perm
523 members.append(users_group_data)
556 members.append(users_group_data)
524
557
525 data = repo.get_api_data()
558 data = repo.get_api_data()
526 data['members'] = members
559 data['members'] = members
527 return data
560 return data
528
561
529 @HasPermissionAnyDecorator('hg.admin')
562 @HasPermissionAllDecorator('hg.admin')
530 def get_repos(self, apiuser):
563 def get_repos(self, apiuser):
531 """"
564 """"
532 Get all repositories
565 Get all repositories
533
566
534 :param apiuser:
567 :param apiuser:
535 """
568 """
536
569
537 result = []
570 result = []
538 for repo in RepoModel().get_all():
571 for repo in RepoModel().get_all():
539 result.append(repo.get_api_data())
572 result.append(repo.get_api_data())
540 return result
573 return result
541
574
542 @HasPermissionAnyDecorator('hg.admin')
575 @HasPermissionAllDecorator('hg.admin')
543 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
576 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
544 ret_type='all'):
577 ret_type='all'):
545 """
578 """
546 returns a list of nodes and it's children
579 returns a list of nodes and it's children
547 for a given path at given revision. It's possible to specify ret_type
580 for a given path at given revision. It's possible to specify ret_type
548 to show only files or dirs
581 to show only files or dirs
549
582
550 :param apiuser:
583 :param apiuser:
551 :param repoid: name or id of repository
584 :param repoid: name or id of repository
552 :param revision: revision for which listing should be done
585 :param revision: revision for which listing should be done
553 :param root_path: path from which start displaying
586 :param root_path: path from which start displaying
554 :param ret_type: return type 'all|files|dirs' nodes
587 :param ret_type: return type 'all|files|dirs' nodes
555 """
588 """
556 repo = get_repo_or_error(repoid)
589 repo = get_repo_or_error(repoid)
557 try:
590 try:
558 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
591 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
559 flat=False)
592 flat=False)
560 _map = {
593 _map = {
561 'all': _d + _f,
594 'all': _d + _f,
562 'files': _f,
595 'files': _f,
563 'dirs': _d,
596 'dirs': _d,
564 }
597 }
565 return _map[ret_type]
598 return _map[ret_type]
566 except KeyError:
599 except KeyError:
567 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
600 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
568 except Exception:
601 except Exception:
569 log.error(traceback.format_exc())
602 log.error(traceback.format_exc())
570 raise JSONRPCError(
603 raise JSONRPCError(
571 'failed to get repo: `%s` nodes' % repo.repo_name
604 'failed to get repo: `%s` nodes' % repo.repo_name
572 )
605 )
573
606
574 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
607 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
575 def create_repo(self, apiuser, repo_name, owner, repo_type=Optional('hg'),
608 def create_repo(self, apiuser, repo_name, owner, repo_type=Optional('hg'),
576 description=Optional(''), private=Optional(False),
609 description=Optional(''), private=Optional(False),
577 clone_uri=Optional(None), landing_rev=Optional('tip'),
610 clone_uri=Optional(None), landing_rev=Optional('tip'),
578 enable_statistics=Optional(False),
611 enable_statistics=Optional(False),
579 enable_locking=Optional(False),
612 enable_locking=Optional(False),
580 enable_downloads=Optional(False)):
613 enable_downloads=Optional(False)):
581 """
614 """
582 Create repository, if clone_url is given it makes a remote clone
615 Create repository, if clone_url is given it makes a remote clone
583 if repo_name is withina group name the groups will be created
616 if repo_name is withina group name the groups will be created
584 automatically if they aren't present
617 automatically if they aren't present
585
618
586 :param apiuser:
619 :param apiuser:
587 :param repo_name:
620 :param repo_name:
588 :param onwer:
621 :param onwer:
589 :param repo_type:
622 :param repo_type:
590 :param description:
623 :param description:
591 :param private:
624 :param private:
592 :param clone_uri:
625 :param clone_uri:
593 :param landing_rev:
626 :param landing_rev:
594 """
627 """
595 owner = get_user_or_error(owner)
628 owner = get_user_or_error(owner)
596
629
597 if RepoModel().get_by_repo_name(repo_name):
630 if RepoModel().get_by_repo_name(repo_name):
598 raise JSONRPCError("repo `%s` already exist" % repo_name)
631 raise JSONRPCError("repo `%s` already exist" % repo_name)
599
632
600 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
633 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
601 if isinstance(private, Optional):
634 if isinstance(private, Optional):
602 private = defs.get('repo_private') or Optional.extract(private)
635 private = defs.get('repo_private') or Optional.extract(private)
603 if isinstance(repo_type, Optional):
636 if isinstance(repo_type, Optional):
604 repo_type = defs.get('repo_type')
637 repo_type = defs.get('repo_type')
605 if isinstance(enable_statistics, Optional):
638 if isinstance(enable_statistics, Optional):
606 enable_statistics = defs.get('repo_enable_statistics')
639 enable_statistics = defs.get('repo_enable_statistics')
607 if isinstance(enable_locking, Optional):
640 if isinstance(enable_locking, Optional):
608 enable_locking = defs.get('repo_enable_locking')
641 enable_locking = defs.get('repo_enable_locking')
609 if isinstance(enable_downloads, Optional):
642 if isinstance(enable_downloads, Optional):
610 enable_downloads = defs.get('repo_enable_downloads')
643 enable_downloads = defs.get('repo_enable_downloads')
611
644
612 clone_uri = Optional.extract(clone_uri)
645 clone_uri = Optional.extract(clone_uri)
613 description = Optional.extract(description)
646 description = Optional.extract(description)
614 landing_rev = Optional.extract(landing_rev)
647 landing_rev = Optional.extract(landing_rev)
615
648
616 try:
649 try:
617 # create structure of groups and return the last group
650 # create structure of groups and return the last group
618 group = map_groups(repo_name)
651 group = map_groups(repo_name)
619
652
620 repo = RepoModel().create_repo(
653 repo = RepoModel().create_repo(
621 repo_name=repo_name,
654 repo_name=repo_name,
622 repo_type=repo_type,
655 repo_type=repo_type,
623 description=description,
656 description=description,
624 owner=owner,
657 owner=owner,
625 private=private,
658 private=private,
626 clone_uri=clone_uri,
659 clone_uri=clone_uri,
627 repos_group=group,
660 repos_group=group,
628 landing_rev=landing_rev,
661 landing_rev=landing_rev,
629 enable_statistics=enable_statistics,
662 enable_statistics=enable_statistics,
630 enable_downloads=enable_downloads,
663 enable_downloads=enable_downloads,
631 enable_locking=enable_locking
664 enable_locking=enable_locking
632 )
665 )
633
666
634 Session().commit()
667 Session().commit()
635
668
636 return dict(
669 return dict(
637 msg="Created new repository `%s`" % (repo.repo_name),
670 msg="Created new repository `%s`" % (repo.repo_name),
638 repo=repo.get_api_data()
671 repo=repo.get_api_data()
639 )
672 )
640
673
641 except Exception:
674 except Exception:
642 log.error(traceback.format_exc())
675 log.error(traceback.format_exc())
643 raise JSONRPCError('failed to create repository `%s`' % repo_name)
676 raise JSONRPCError('failed to create repository `%s`' % repo_name)
644
677
645 @HasPermissionAnyDecorator('hg.admin')
678 @HasPermissionAllDecorator('hg.admin')
646 def fork_repo(self, apiuser, repoid, fork_name, owner,
679 def fork_repo(self, apiuser, repoid, fork_name, owner,
647 description=Optional(''), copy_permissions=Optional(False),
680 description=Optional(''), copy_permissions=Optional(False),
648 private=Optional(False), landing_rev=Optional('tip')):
681 private=Optional(False), landing_rev=Optional('tip')):
649 repo = get_repo_or_error(repoid)
682 repo = get_repo_or_error(repoid)
650 repo_name = repo.repo_name
683 repo_name = repo.repo_name
651 owner = get_user_or_error(owner)
684 owner = get_user_or_error(owner)
652
685
653 _repo = RepoModel().get_by_repo_name(fork_name)
686 _repo = RepoModel().get_by_repo_name(fork_name)
654 if _repo:
687 if _repo:
655 type_ = 'fork' if _repo.fork else 'repo'
688 type_ = 'fork' if _repo.fork else 'repo'
656 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
689 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
657
690
658 try:
691 try:
659 # create structure of groups and return the last group
692 # create structure of groups and return the last group
660 group = map_groups(fork_name)
693 group = map_groups(fork_name)
661
694
662 form_data = dict(
695 form_data = dict(
663 repo_name=fork_name,
696 repo_name=fork_name,
664 repo_name_full=fork_name,
697 repo_name_full=fork_name,
665 repo_group=group,
698 repo_group=group,
666 repo_type=repo.repo_type,
699 repo_type=repo.repo_type,
667 description=Optional.extract(description),
700 description=Optional.extract(description),
668 private=Optional.extract(private),
701 private=Optional.extract(private),
669 copy_permissions=Optional.extract(copy_permissions),
702 copy_permissions=Optional.extract(copy_permissions),
670 landing_rev=Optional.extract(landing_rev),
703 landing_rev=Optional.extract(landing_rev),
671 update_after_clone=False,
704 update_after_clone=False,
672 fork_parent_id=repo.repo_id,
705 fork_parent_id=repo.repo_id,
673 )
706 )
674 RepoModel().create_fork(form_data, cur_user=owner)
707 RepoModel().create_fork(form_data, cur_user=owner)
675 return dict(
708 return dict(
676 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
709 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
677 fork_name),
710 fork_name),
678 success=True # cannot return the repo data here since fork
711 success=True # cannot return the repo data here since fork
679 # cann be done async
712 # cann be done async
680 )
713 )
681 except Exception:
714 except Exception:
682 log.error(traceback.format_exc())
715 log.error(traceback.format_exc())
683 raise JSONRPCError(
716 raise JSONRPCError(
684 'failed to fork repository `%s` as `%s`' % (repo_name,
717 'failed to fork repository `%s` as `%s`' % (repo_name,
685 fork_name)
718 fork_name)
686 )
719 )
687
720
688 @HasPermissionAnyDecorator('hg.admin')
721 @HasPermissionAllDecorator('hg.admin')
689 def delete_repo(self, apiuser, repoid):
722 def delete_repo(self, apiuser, repoid):
690 """
723 """
691 Deletes a given repository
724 Deletes a given repository
692
725
693 :param apiuser:
726 :param apiuser:
694 :param repoid:
727 :param repoid:
695 """
728 """
696 repo = get_repo_or_error(repoid)
729 repo = get_repo_or_error(repoid)
697
730
698 try:
731 try:
699 RepoModel().delete(repo)
732 RepoModel().delete(repo)
700 Session().commit()
733 Session().commit()
701 return dict(
734 return dict(
702 msg='Deleted repository `%s`' % repo.repo_name,
735 msg='Deleted repository `%s`' % repo.repo_name,
703 success=True
736 success=True
704 )
737 )
705 except Exception:
738 except Exception:
706 log.error(traceback.format_exc())
739 log.error(traceback.format_exc())
707 raise JSONRPCError(
740 raise JSONRPCError(
708 'failed to delete repository `%s`' % repo.repo_name
741 'failed to delete repository `%s`' % repo.repo_name
709 )
742 )
710
743
711 @HasPermissionAnyDecorator('hg.admin')
744 @HasPermissionAllDecorator('hg.admin')
712 def grant_user_permission(self, apiuser, repoid, userid, perm):
745 def grant_user_permission(self, apiuser, repoid, userid, perm):
713 """
746 """
714 Grant permission for user on given repository, or update existing one
747 Grant permission for user on given repository, or update existing one
715 if found
748 if found
716
749
717 :param repoid:
750 :param repoid:
718 :param userid:
751 :param userid:
719 :param perm:
752 :param perm:
720 """
753 """
721 repo = get_repo_or_error(repoid)
754 repo = get_repo_or_error(repoid)
722 user = get_user_or_error(userid)
755 user = get_user_or_error(userid)
723 perm = get_perm_or_error(perm)
756 perm = get_perm_or_error(perm)
724
757
725 try:
758 try:
726
759
727 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
760 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
728
761
729 Session().commit()
762 Session().commit()
730 return dict(
763 return dict(
731 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
764 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
732 perm.permission_name, user.username, repo.repo_name
765 perm.permission_name, user.username, repo.repo_name
733 ),
766 ),
734 success=True
767 success=True
735 )
768 )
736 except Exception:
769 except Exception:
737 log.error(traceback.format_exc())
770 log.error(traceback.format_exc())
738 raise JSONRPCError(
771 raise JSONRPCError(
739 'failed to edit permission for user: `%s` in repo: `%s`' % (
772 'failed to edit permission for user: `%s` in repo: `%s`' % (
740 userid, repoid
773 userid, repoid
741 )
774 )
742 )
775 )
743
776
744 @HasPermissionAnyDecorator('hg.admin')
777 @HasPermissionAllDecorator('hg.admin')
745 def revoke_user_permission(self, apiuser, repoid, userid):
778 def revoke_user_permission(self, apiuser, repoid, userid):
746 """
779 """
747 Revoke permission for user on given repository
780 Revoke permission for user on given repository
748
781
749 :param apiuser:
782 :param apiuser:
750 :param repoid:
783 :param repoid:
751 :param userid:
784 :param userid:
752 """
785 """
753
786
754 repo = get_repo_or_error(repoid)
787 repo = get_repo_or_error(repoid)
755 user = get_user_or_error(userid)
788 user = get_user_or_error(userid)
756 try:
789 try:
757
790
758 RepoModel().revoke_user_permission(repo=repo, user=user)
791 RepoModel().revoke_user_permission(repo=repo, user=user)
759
792
760 Session().commit()
793 Session().commit()
761 return dict(
794 return dict(
762 msg='Revoked perm for user: `%s` in repo: `%s`' % (
795 msg='Revoked perm for user: `%s` in repo: `%s`' % (
763 user.username, repo.repo_name
796 user.username, repo.repo_name
764 ),
797 ),
765 success=True
798 success=True
766 )
799 )
767 except Exception:
800 except Exception:
768 log.error(traceback.format_exc())
801 log.error(traceback.format_exc())
769 raise JSONRPCError(
802 raise JSONRPCError(
770 'failed to edit permission for user: `%s` in repo: `%s`' % (
803 'failed to edit permission for user: `%s` in repo: `%s`' % (
771 userid, repoid
804 userid, repoid
772 )
805 )
773 )
806 )
774
807
775 @HasPermissionAnyDecorator('hg.admin')
808 @HasPermissionAllDecorator('hg.admin')
776 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
809 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
777 perm):
810 perm):
778 """
811 """
779 Grant permission for users group on given repository, or update
812 Grant permission for users group on given repository, or update
780 existing one if found
813 existing one if found
781
814
782 :param apiuser:
815 :param apiuser:
783 :param repoid:
816 :param repoid:
784 :param usersgroupid:
817 :param usersgroupid:
785 :param perm:
818 :param perm:
786 """
819 """
787 repo = get_repo_or_error(repoid)
820 repo = get_repo_or_error(repoid)
788 perm = get_perm_or_error(perm)
821 perm = get_perm_or_error(perm)
789 users_group = get_users_group_or_error(usersgroupid)
822 users_group = get_users_group_or_error(usersgroupid)
790
823
791 try:
824 try:
792 RepoModel().grant_users_group_permission(repo=repo,
825 RepoModel().grant_users_group_permission(repo=repo,
793 group_name=users_group,
826 group_name=users_group,
794 perm=perm)
827 perm=perm)
795
828
796 Session().commit()
829 Session().commit()
797 return dict(
830 return dict(
798 msg='Granted perm: `%s` for users group: `%s` in '
831 msg='Granted perm: `%s` for users group: `%s` in '
799 'repo: `%s`' % (
832 'repo: `%s`' % (
800 perm.permission_name, users_group.users_group_name,
833 perm.permission_name, users_group.users_group_name,
801 repo.repo_name
834 repo.repo_name
802 ),
835 ),
803 success=True
836 success=True
804 )
837 )
805 except Exception:
838 except Exception:
806 log.error(traceback.format_exc())
839 log.error(traceback.format_exc())
807 raise JSONRPCError(
840 raise JSONRPCError(
808 'failed to edit permission for users group: `%s` in '
841 'failed to edit permission for users group: `%s` in '
809 'repo: `%s`' % (
842 'repo: `%s`' % (
810 usersgroupid, repo.repo_name
843 usersgroupid, repo.repo_name
811 )
844 )
812 )
845 )
813
846
814 @HasPermissionAnyDecorator('hg.admin')
847 @HasPermissionAllDecorator('hg.admin')
815 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
848 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
816 """
849 """
817 Revoke permission for users group on given repository
850 Revoke permission for users group on given repository
818
851
819 :param apiuser:
852 :param apiuser:
820 :param repoid:
853 :param repoid:
821 :param usersgroupid:
854 :param usersgroupid:
822 """
855 """
823 repo = get_repo_or_error(repoid)
856 repo = get_repo_or_error(repoid)
824 users_group = get_users_group_or_error(usersgroupid)
857 users_group = get_users_group_or_error(usersgroupid)
825
858
826 try:
859 try:
827 RepoModel().revoke_users_group_permission(repo=repo,
860 RepoModel().revoke_users_group_permission(repo=repo,
828 group_name=users_group)
861 group_name=users_group)
829
862
830 Session().commit()
863 Session().commit()
831 return dict(
864 return dict(
832 msg='Revoked perm for users group: `%s` in repo: `%s`' % (
865 msg='Revoked perm for users group: `%s` in repo: `%s`' % (
833 users_group.users_group_name, repo.repo_name
866 users_group.users_group_name, repo.repo_name
834 ),
867 ),
835 success=True
868 success=True
836 )
869 )
837 except Exception:
870 except Exception:
838 log.error(traceback.format_exc())
871 log.error(traceback.format_exc())
839 raise JSONRPCError(
872 raise JSONRPCError(
840 'failed to edit permission for users group: `%s` in '
873 'failed to edit permission for users group: `%s` in '
841 'repo: `%s`' % (
874 'repo: `%s`' % (
842 users_group.users_group_name, repo.repo_name
875 users_group.users_group_name, repo.repo_name
843 )
876 )
844 )
877 )
@@ -1,879 +1,982 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import random
26 import random
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import hashlib
29 import hashlib
30
30
31 from tempfile import _RandomNameSequence
31 from tempfile import _RandomNameSequence
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, url, request
34 from pylons import config, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode import __platform__, is_windows, is_unix
38 from rhodecode import __platform__, is_windows, is_unix
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40
40
41 from rhodecode.lib.utils2 import str2bool, safe_unicode
41 from rhodecode.lib.utils2 import str2bool, safe_unicode
42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
44 from rhodecode.lib.auth_ldap import AuthLdap
44 from rhodecode.lib.auth_ldap import AuthLdap
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
48 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
48 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
49 from rhodecode.lib.caching_query import FromCache
49 from rhodecode.lib.caching_query import FromCache
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 class PasswordGenerator(object):
54 class PasswordGenerator(object):
55 """
55 """
56 This is a simple class for generating password from different sets of
56 This is a simple class for generating password from different sets of
57 characters
57 characters
58 usage::
58 usage::
59
59
60 passwd_gen = PasswordGenerator()
60 passwd_gen = PasswordGenerator()
61 #print 8-letter password containing only big and small letters
61 #print 8-letter password containing only big and small letters
62 of alphabet
62 of alphabet
63 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
63 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 """
64 """
65 ALPHABETS_NUM = r'''1234567890'''
65 ALPHABETS_NUM = r'''1234567890'''
66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
70 + ALPHABETS_NUM + ALPHABETS_SPECIAL
70 + ALPHABETS_NUM + ALPHABETS_SPECIAL
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
75
75
76 def __init__(self, passwd=''):
76 def __init__(self, passwd=''):
77 self.passwd = passwd
77 self.passwd = passwd
78
78
79 def gen_password(self, length, type_=None):
79 def gen_password(self, length, type_=None):
80 if type_ is None:
80 if type_ is None:
81 type_ = self.ALPHABETS_FULL
81 type_ = self.ALPHABETS_FULL
82 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
82 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
83 return self.passwd
83 return self.passwd
84
84
85
85
86 class RhodeCodeCrypto(object):
86 class RhodeCodeCrypto(object):
87
87
88 @classmethod
88 @classmethod
89 def hash_string(cls, str_):
89 def hash_string(cls, str_):
90 """
90 """
91 Cryptographic function used for password hashing based on pybcrypt
91 Cryptographic function used for password hashing based on pybcrypt
92 or pycrypto in windows
92 or pycrypto in windows
93
93
94 :param password: password to hash
94 :param password: password to hash
95 """
95 """
96 if is_windows:
96 if is_windows:
97 from hashlib import sha256
97 from hashlib import sha256
98 return sha256(str_).hexdigest()
98 return sha256(str_).hexdigest()
99 elif is_unix:
99 elif is_unix:
100 import bcrypt
100 import bcrypt
101 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
101 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
102 else:
102 else:
103 raise Exception('Unknown or unsupported platform %s' \
103 raise Exception('Unknown or unsupported platform %s' \
104 % __platform__)
104 % __platform__)
105
105
106 @classmethod
106 @classmethod
107 def hash_check(cls, password, hashed):
107 def hash_check(cls, password, hashed):
108 """
108 """
109 Checks matching password with it's hashed value, runs different
109 Checks matching password with it's hashed value, runs different
110 implementation based on platform it runs on
110 implementation based on platform it runs on
111
111
112 :param password: password
112 :param password: password
113 :param hashed: password in hashed form
113 :param hashed: password in hashed form
114 """
114 """
115
115
116 if is_windows:
116 if is_windows:
117 from hashlib import sha256
117 from hashlib import sha256
118 return sha256(password).hexdigest() == hashed
118 return sha256(password).hexdigest() == hashed
119 elif is_unix:
119 elif is_unix:
120 import bcrypt
120 import bcrypt
121 return bcrypt.hashpw(password, hashed) == hashed
121 return bcrypt.hashpw(password, hashed) == hashed
122 else:
122 else:
123 raise Exception('Unknown or unsupported platform %s' \
123 raise Exception('Unknown or unsupported platform %s' \
124 % __platform__)
124 % __platform__)
125
125
126
126
127 def get_crypt_password(password):
127 def get_crypt_password(password):
128 return RhodeCodeCrypto.hash_string(password)
128 return RhodeCodeCrypto.hash_string(password)
129
129
130
130
131 def check_password(password, hashed):
131 def check_password(password, hashed):
132 return RhodeCodeCrypto.hash_check(password, hashed)
132 return RhodeCodeCrypto.hash_check(password, hashed)
133
133
134
134
135 def generate_api_key(str_, salt=None):
135 def generate_api_key(str_, salt=None):
136 """
136 """
137 Generates API KEY from given string
137 Generates API KEY from given string
138
138
139 :param str_:
139 :param str_:
140 :param salt:
140 :param salt:
141 """
141 """
142
142
143 if salt is None:
143 if salt is None:
144 salt = _RandomNameSequence().next()
144 salt = _RandomNameSequence().next()
145
145
146 return hashlib.sha1(str_ + salt).hexdigest()
146 return hashlib.sha1(str_ + salt).hexdigest()
147
147
148
148
149 def authfunc(environ, username, password):
149 def authfunc(environ, username, password):
150 """
150 """
151 Dummy authentication wrapper function used in Mercurial and Git for
151 Dummy authentication wrapper function used in Mercurial and Git for
152 access control.
152 access control.
153
153
154 :param environ: needed only for using in Basic auth
154 :param environ: needed only for using in Basic auth
155 """
155 """
156 return authenticate(username, password)
156 return authenticate(username, password)
157
157
158
158
159 def authenticate(username, password):
159 def authenticate(username, password):
160 """
160 """
161 Authentication function used for access control,
161 Authentication function used for access control,
162 firstly checks for db authentication then if ldap is enabled for ldap
162 firstly checks for db authentication then if ldap is enabled for ldap
163 authentication, also creates ldap user if not in database
163 authentication, also creates ldap user if not in database
164
164
165 :param username: username
165 :param username: username
166 :param password: password
166 :param password: password
167 """
167 """
168
168
169 user_model = UserModel()
169 user_model = UserModel()
170 user = User.get_by_username(username)
170 user = User.get_by_username(username)
171
171
172 log.debug('Authenticating user using RhodeCode account')
172 log.debug('Authenticating user using RhodeCode account')
173 if user is not None and not user.ldap_dn:
173 if user is not None and not user.ldap_dn:
174 if user.active:
174 if user.active:
175 if user.username == 'default' and user.active:
175 if user.username == 'default' and user.active:
176 log.info('user %s authenticated correctly as anonymous user' %
176 log.info('user %s authenticated correctly as anonymous user' %
177 username)
177 username)
178 return True
178 return True
179
179
180 elif user.username == username and check_password(password,
180 elif user.username == username and check_password(password,
181 user.password):
181 user.password):
182 log.info('user %s authenticated correctly' % username)
182 log.info('user %s authenticated correctly' % username)
183 return True
183 return True
184 else:
184 else:
185 log.warning('user %s tried auth but is disabled' % username)
185 log.warning('user %s tried auth but is disabled' % username)
186
186
187 else:
187 else:
188 log.debug('Regular authentication failed')
188 log.debug('Regular authentication failed')
189 user_obj = User.get_by_username(username, case_insensitive=True)
189 user_obj = User.get_by_username(username, case_insensitive=True)
190
190
191 if user_obj is not None and not user_obj.ldap_dn:
191 if user_obj is not None and not user_obj.ldap_dn:
192 log.debug('this user already exists as non ldap')
192 log.debug('this user already exists as non ldap')
193 return False
193 return False
194
194
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
196 #======================================================================
196 #======================================================================
197 # FALLBACK TO LDAP AUTH IF ENABLE
197 # FALLBACK TO LDAP AUTH IF ENABLE
198 #======================================================================
198 #======================================================================
199 if str2bool(ldap_settings.get('ldap_active')):
199 if str2bool(ldap_settings.get('ldap_active')):
200 log.debug("Authenticating user using ldap")
200 log.debug("Authenticating user using ldap")
201 kwargs = {
201 kwargs = {
202 'server': ldap_settings.get('ldap_host', ''),
202 'server': ldap_settings.get('ldap_host', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
204 'port': ldap_settings.get('ldap_port'),
204 'port': ldap_settings.get('ldap_port'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
212 'ldap_version': 3,
212 'ldap_version': 3,
213 }
213 }
214 log.debug('Checking for ldap authentication')
214 log.debug('Checking for ldap authentication')
215 try:
215 try:
216 aldap = AuthLdap(**kwargs)
216 aldap = AuthLdap(**kwargs)
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
218 password)
218 password)
219 log.debug('Got ldap DN response %s' % user_dn)
219 log.debug('Got ldap DN response %s' % user_dn)
220
220
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
222 .get(k), [''])[0]
222 .get(k), [''])[0]
223
223
224 user_attrs = {
224 user_attrs = {
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
227 'email': get_ldap_attr('ldap_attr_email'),
227 'email': get_ldap_attr('ldap_attr_email'),
228 }
228 }
229
229
230 # don't store LDAP password since we don't need it. Override
230 # don't store LDAP password since we don't need it. Override
231 # with some random generated password
231 # with some random generated password
232 _password = PasswordGenerator().gen_password(length=8)
232 _password = PasswordGenerator().gen_password(length=8)
233 # create this user on the fly if it doesn't exist in rhodecode
233 # create this user on the fly if it doesn't exist in rhodecode
234 # database
234 # database
235 if user_model.create_ldap(username, _password, user_dn,
235 if user_model.create_ldap(username, _password, user_dn,
236 user_attrs):
236 user_attrs):
237 log.info('created new ldap user %s' % username)
237 log.info('created new ldap user %s' % username)
238
238
239 Session().commit()
239 Session().commit()
240 return True
240 return True
241 except (LdapUsernameError, LdapPasswordError,):
241 except (LdapUsernameError, LdapPasswordError,):
242 pass
242 pass
243 except (Exception,):
243 except (Exception,):
244 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
245 pass
245 pass
246 return False
246 return False
247
247
248
248
249 def login_container_auth(username):
249 def login_container_auth(username):
250 user = User.get_by_username(username)
250 user = User.get_by_username(username)
251 if user is None:
251 if user is None:
252 user_attrs = {
252 user_attrs = {
253 'name': username,
253 'name': username,
254 'lastname': None,
254 'lastname': None,
255 'email': None,
255 'email': None,
256 }
256 }
257 user = UserModel().create_for_container_auth(username, user_attrs)
257 user = UserModel().create_for_container_auth(username, user_attrs)
258 if not user:
258 if not user:
259 return None
259 return None
260 log.info('User %s was created by container authentication' % username)
260 log.info('User %s was created by container authentication' % username)
261
261
262 if not user.active:
262 if not user.active:
263 return None
263 return None
264
264
265 user.update_lastlogin()
265 user.update_lastlogin()
266 Session().commit()
266 Session().commit()
267
267
268 log.debug('User %s is now logged in by container authentication',
268 log.debug('User %s is now logged in by container authentication',
269 user.username)
269 user.username)
270 return user
270 return user
271
271
272
272
273 def get_container_username(environ, config):
273 def get_container_username(environ, config):
274 username = None
274 username = None
275
275
276 if str2bool(config.get('container_auth_enabled', False)):
276 if str2bool(config.get('container_auth_enabled', False)):
277 from paste.httpheaders import REMOTE_USER
277 from paste.httpheaders import REMOTE_USER
278 username = REMOTE_USER(environ)
278 username = REMOTE_USER(environ)
279
279
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
281 username = environ.get('HTTP_X_FORWARDED_USER')
281 username = environ.get('HTTP_X_FORWARDED_USER')
282
282
283 if username:
283 if username:
284 # Removing realm and domain from username
284 # Removing realm and domain from username
285 username = username.partition('@')[0]
285 username = username.partition('@')[0]
286 username = username.rpartition('\\')[2]
286 username = username.rpartition('\\')[2]
287 log.debug('Received username %s from container' % username)
287 log.debug('Received username %s from container' % username)
288
288
289 return username
289 return username
290
290
291
291
292 class CookieStoreWrapper(object):
292 class CookieStoreWrapper(object):
293
293
294 def __init__(self, cookie_store):
294 def __init__(self, cookie_store):
295 self.cookie_store = cookie_store
295 self.cookie_store = cookie_store
296
296
297 def __repr__(self):
297 def __repr__(self):
298 return 'CookieStore<%s>' % (self.cookie_store)
298 return 'CookieStore<%s>' % (self.cookie_store)
299
299
300 def get(self, key, other=None):
300 def get(self, key, other=None):
301 if isinstance(self.cookie_store, dict):
301 if isinstance(self.cookie_store, dict):
302 return self.cookie_store.get(key, other)
302 return self.cookie_store.get(key, other)
303 elif isinstance(self.cookie_store, AuthUser):
303 elif isinstance(self.cookie_store, AuthUser):
304 return self.cookie_store.__dict__.get(key, other)
304 return self.cookie_store.__dict__.get(key, other)
305
305
306
306
307 class AuthUser(object):
307 class AuthUser(object):
308 """
308 """
309 A simple object that handles all attributes of user in RhodeCode
309 A simple object that handles all attributes of user in RhodeCode
310
310
311 It does lookup based on API key,given user, or user present in session
311 It does lookup based on API key,given user, or user present in session
312 Then it fills all required information for such user. It also checks if
312 Then it fills all required information for such user. It also checks if
313 anonymous access is enabled and if so, it returns default user as logged
313 anonymous access is enabled and if so, it returns default user as logged
314 in
314 in
315 """
315 """
316
316
317 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
317 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
318
318
319 self.user_id = user_id
319 self.user_id = user_id
320 self.api_key = None
320 self.api_key = None
321 self.username = username
321 self.username = username
322 self.ip_addr = ip_addr
322 self.ip_addr = ip_addr
323
323
324 self.name = ''
324 self.name = ''
325 self.lastname = ''
325 self.lastname = ''
326 self.email = ''
326 self.email = ''
327 self.is_authenticated = False
327 self.is_authenticated = False
328 self.admin = False
328 self.admin = False
329 self.inherit_default_permissions = False
329 self.inherit_default_permissions = False
330 self.permissions = {}
330 self.permissions = {}
331 self._api_key = api_key
331 self._api_key = api_key
332 self.propagate_data()
332 self.propagate_data()
333 self._instance = None
333 self._instance = None
334
334
335 def propagate_data(self):
335 def propagate_data(self):
336 user_model = UserModel()
336 user_model = UserModel()
337 self.anonymous_user = User.get_by_username('default', cache=True)
337 self.anonymous_user = User.get_by_username('default', cache=True)
338 is_user_loaded = False
338 is_user_loaded = False
339
339
340 # try go get user by api key
340 # try go get user by api key
341 if self._api_key and self._api_key != self.anonymous_user.api_key:
341 if self._api_key and self._api_key != self.anonymous_user.api_key:
342 log.debug('Auth User lookup by API KEY %s' % self._api_key)
342 log.debug('Auth User lookup by API KEY %s' % self._api_key)
343 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
343 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
344 # lookup by userid
344 # lookup by userid
345 elif (self.user_id is not None and
345 elif (self.user_id is not None and
346 self.user_id != self.anonymous_user.user_id):
346 self.user_id != self.anonymous_user.user_id):
347 log.debug('Auth User lookup by USER ID %s' % self.user_id)
347 log.debug('Auth User lookup by USER ID %s' % self.user_id)
348 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
348 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
349 # lookup by username
349 # lookup by username
350 elif self.username and \
350 elif self.username and \
351 str2bool(config.get('container_auth_enabled', False)):
351 str2bool(config.get('container_auth_enabled', False)):
352
352
353 log.debug('Auth User lookup by USER NAME %s' % self.username)
353 log.debug('Auth User lookup by USER NAME %s' % self.username)
354 dbuser = login_container_auth(self.username)
354 dbuser = login_container_auth(self.username)
355 if dbuser is not None:
355 if dbuser is not None:
356 log.debug('filling all attributes to object')
356 log.debug('filling all attributes to object')
357 for k, v in dbuser.get_dict().items():
357 for k, v in dbuser.get_dict().items():
358 setattr(self, k, v)
358 setattr(self, k, v)
359 self.set_authenticated()
359 self.set_authenticated()
360 is_user_loaded = True
360 is_user_loaded = True
361 else:
361 else:
362 log.debug('No data in %s that could been used to log in' % self)
362 log.debug('No data in %s that could been used to log in' % self)
363
363
364 if not is_user_loaded:
364 if not is_user_loaded:
365 # if we cannot authenticate user try anonymous
365 # if we cannot authenticate user try anonymous
366 if self.anonymous_user.active is True:
366 if self.anonymous_user.active is True:
367 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
367 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
368 # then we set this user is logged in
368 # then we set this user is logged in
369 self.is_authenticated = True
369 self.is_authenticated = True
370 else:
370 else:
371 self.user_id = None
371 self.user_id = None
372 self.username = None
372 self.username = None
373 self.is_authenticated = False
373 self.is_authenticated = False
374
374
375 if not self.username:
375 if not self.username:
376 self.username = 'None'
376 self.username = 'None'
377
377
378 log.debug('Auth User is now %s' % self)
378 log.debug('Auth User is now %s' % self)
379 user_model.fill_perms(self)
379 user_model.fill_perms(self)
380
380
381 @property
381 @property
382 def is_admin(self):
382 def is_admin(self):
383 return self.admin
383 return self.admin
384
384
385 @property
385 @property
386 def ip_allowed(self):
386 def ip_allowed(self):
387 """
387 """
388 Checks if ip_addr used in constructor is allowed from defined list of
388 Checks if ip_addr used in constructor is allowed from defined list of
389 allowed ip_addresses for user
389 allowed ip_addresses for user
390
390
391 :returns: boolean, True if ip is in allowed ip range
391 :returns: boolean, True if ip is in allowed ip range
392 """
392 """
393 #check IP
393 #check IP
394 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
394 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
395 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
395 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
396 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
396 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
397 return True
397 return True
398 else:
398 else:
399 log.info('Access for IP:%s forbidden, '
399 log.info('Access for IP:%s forbidden, '
400 'not in %s' % (self.ip_addr, allowed_ips))
400 'not in %s' % (self.ip_addr, allowed_ips))
401 return False
401 return False
402
402
403 def __repr__(self):
403 def __repr__(self):
404 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
404 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
405 self.is_authenticated)
405 self.is_authenticated)
406
406
407 def set_authenticated(self, authenticated=True):
407 def set_authenticated(self, authenticated=True):
408 if self.user_id != self.anonymous_user.user_id:
408 if self.user_id != self.anonymous_user.user_id:
409 self.is_authenticated = authenticated
409 self.is_authenticated = authenticated
410
410
411 def get_cookie_store(self):
411 def get_cookie_store(self):
412 return {'username': self.username,
412 return {'username': self.username,
413 'user_id': self.user_id,
413 'user_id': self.user_id,
414 'is_authenticated': self.is_authenticated}
414 'is_authenticated': self.is_authenticated}
415
415
416 @classmethod
416 @classmethod
417 def from_cookie_store(cls, cookie_store):
417 def from_cookie_store(cls, cookie_store):
418 """
418 """
419 Creates AuthUser from a cookie store
419 Creates AuthUser from a cookie store
420
420
421 :param cls:
421 :param cls:
422 :param cookie_store:
422 :param cookie_store:
423 """
423 """
424 user_id = cookie_store.get('user_id')
424 user_id = cookie_store.get('user_id')
425 username = cookie_store.get('username')
425 username = cookie_store.get('username')
426 api_key = cookie_store.get('api_key')
426 api_key = cookie_store.get('api_key')
427 return AuthUser(user_id, api_key, username)
427 return AuthUser(user_id, api_key, username)
428
428
429 @classmethod
429 @classmethod
430 def get_allowed_ips(cls, user_id, cache=False):
430 def get_allowed_ips(cls, user_id, cache=False):
431 _set = set()
431 _set = set()
432 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
432 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
433 if cache:
433 if cache:
434 user_ips = user_ips.options(FromCache("sql_cache_short",
434 user_ips = user_ips.options(FromCache("sql_cache_short",
435 "get_user_ips_%s" % user_id))
435 "get_user_ips_%s" % user_id))
436 for ip in user_ips:
436 for ip in user_ips:
437 _set.add(ip.ip_addr)
437 _set.add(ip.ip_addr)
438 return _set or set(['0.0.0.0/0'])
438 return _set or set(['0.0.0.0/0'])
439
439
440
440
441 def set_available_permissions(config):
441 def set_available_permissions(config):
442 """
442 """
443 This function will propagate pylons globals with all available defined
443 This function will propagate pylons globals with all available defined
444 permission given in db. We don't want to check each time from db for new
444 permission given in db. We don't want to check each time from db for new
445 permissions since adding a new permission also requires application restart
445 permissions since adding a new permission also requires application restart
446 ie. to decorate new views with the newly created permission
446 ie. to decorate new views with the newly created permission
447
447
448 :param config: current pylons config instance
448 :param config: current pylons config instance
449
449
450 """
450 """
451 log.info('getting information about all available permissions')
451 log.info('getting information about all available permissions')
452 try:
452 try:
453 sa = meta.Session
453 sa = meta.Session
454 all_perms = sa.query(Permission).all()
454 all_perms = sa.query(Permission).all()
455 except Exception:
455 except Exception:
456 pass
456 pass
457 finally:
457 finally:
458 meta.Session.remove()
458 meta.Session.remove()
459
459
460 config['available_permissions'] = [x.permission_name for x in all_perms]
460 config['available_permissions'] = [x.permission_name for x in all_perms]
461
461
462
462
463 #==============================================================================
463 #==============================================================================
464 # CHECK DECORATORS
464 # CHECK DECORATORS
465 #==============================================================================
465 #==============================================================================
466 class LoginRequired(object):
466 class LoginRequired(object):
467 """
467 """
468 Must be logged in to execute this function else
468 Must be logged in to execute this function else
469 redirect to login page
469 redirect to login page
470
470
471 :param api_access: if enabled this checks only for valid auth token
471 :param api_access: if enabled this checks only for valid auth token
472 and grants access based on valid token
472 and grants access based on valid token
473 """
473 """
474
474
475 def __init__(self, api_access=False):
475 def __init__(self, api_access=False):
476 self.api_access = api_access
476 self.api_access = api_access
477
477
478 def __call__(self, func):
478 def __call__(self, func):
479 return decorator(self.__wrapper, func)
479 return decorator(self.__wrapper, func)
480
480
481 def __wrapper(self, func, *fargs, **fkwargs):
481 def __wrapper(self, func, *fargs, **fkwargs):
482 cls = fargs[0]
482 cls = fargs[0]
483 user = cls.rhodecode_user
483 user = cls.rhodecode_user
484 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
484 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
485
485
486 #check IP
486 #check IP
487 ip_access_ok = True
487 ip_access_ok = True
488 if not user.ip_allowed:
488 if not user.ip_allowed:
489 from rhodecode.lib import helpers as h
489 from rhodecode.lib import helpers as h
490 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
490 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
491 category='warning')
491 category='warning')
492 ip_access_ok = False
492 ip_access_ok = False
493
493
494 api_access_ok = False
494 api_access_ok = False
495 if self.api_access:
495 if self.api_access:
496 log.debug('Checking API KEY access for %s' % cls)
496 log.debug('Checking API KEY access for %s' % cls)
497 if user.api_key == request.GET.get('api_key'):
497 if user.api_key == request.GET.get('api_key'):
498 api_access_ok = True
498 api_access_ok = True
499 else:
499 else:
500 log.debug("API KEY token not valid")
500 log.debug("API KEY token not valid")
501
501
502 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
502 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
503 if (user.is_authenticated or api_access_ok) and ip_access_ok:
503 if (user.is_authenticated or api_access_ok) and ip_access_ok:
504 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
504 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
505 log.info('user %s is authenticated and granted access to %s '
505 log.info('user %s is authenticated and granted access to %s '
506 'using %s' % (user.username, loc, reason)
506 'using %s' % (user.username, loc, reason)
507 )
507 )
508 return func(*fargs, **fkwargs)
508 return func(*fargs, **fkwargs)
509 else:
509 else:
510 log.warn('user %s NOT authenticated on func: %s' % (
510 log.warn('user %s NOT authenticated on func: %s' % (
511 user, loc)
511 user, loc)
512 )
512 )
513 p = url.current()
513 p = url.current()
514
514
515 log.debug('redirecting to login page with %s' % p)
515 log.debug('redirecting to login page with %s' % p)
516 return redirect(url('login_home', came_from=p))
516 return redirect(url('login_home', came_from=p))
517
517
518
518
519 class NotAnonymous(object):
519 class NotAnonymous(object):
520 """
520 """
521 Must be logged in to execute this function else
521 Must be logged in to execute this function else
522 redirect to login page"""
522 redirect to login page"""
523
523
524 def __call__(self, func):
524 def __call__(self, func):
525 return decorator(self.__wrapper, func)
525 return decorator(self.__wrapper, func)
526
526
527 def __wrapper(self, func, *fargs, **fkwargs):
527 def __wrapper(self, func, *fargs, **fkwargs):
528 cls = fargs[0]
528 cls = fargs[0]
529 self.user = cls.rhodecode_user
529 self.user = cls.rhodecode_user
530
530
531 log.debug('Checking if user is not anonymous @%s' % cls)
531 log.debug('Checking if user is not anonymous @%s' % cls)
532
532
533 anonymous = self.user.username == 'default'
533 anonymous = self.user.username == 'default'
534
534
535 if anonymous:
535 if anonymous:
536 p = url.current()
536 p = url.current()
537
537
538 import rhodecode.lib.helpers as h
538 import rhodecode.lib.helpers as h
539 h.flash(_('You need to be a registered user to '
539 h.flash(_('You need to be a registered user to '
540 'perform this action'),
540 'perform this action'),
541 category='warning')
541 category='warning')
542 return redirect(url('login_home', came_from=p))
542 return redirect(url('login_home', came_from=p))
543 else:
543 else:
544 return func(*fargs, **fkwargs)
544 return func(*fargs, **fkwargs)
545
545
546
546
547 class PermsDecorator(object):
547 class PermsDecorator(object):
548 """Base class for controller decorators"""
548 """Base class for controller decorators"""
549
549
550 def __init__(self, *required_perms):
550 def __init__(self, *required_perms):
551 available_perms = config['available_permissions']
551 available_perms = config['available_permissions']
552 for perm in required_perms:
552 for perm in required_perms:
553 if perm not in available_perms:
553 if perm not in available_perms:
554 raise Exception("'%s' permission is not defined" % perm)
554 raise Exception("'%s' permission is not defined" % perm)
555 self.required_perms = set(required_perms)
555 self.required_perms = set(required_perms)
556 self.user_perms = None
556 self.user_perms = None
557
557
558 def __call__(self, func):
558 def __call__(self, func):
559 return decorator(self.__wrapper, func)
559 return decorator(self.__wrapper, func)
560
560
561 def __wrapper(self, func, *fargs, **fkwargs):
561 def __wrapper(self, func, *fargs, **fkwargs):
562 cls = fargs[0]
562 cls = fargs[0]
563 self.user = cls.rhodecode_user
563 self.user = cls.rhodecode_user
564 self.user_perms = self.user.permissions
564 self.user_perms = self.user.permissions
565 log.debug('checking %s permissions %s for %s %s',
565 log.debug('checking %s permissions %s for %s %s',
566 self.__class__.__name__, self.required_perms, cls, self.user)
566 self.__class__.__name__, self.required_perms, cls, self.user)
567
567
568 if self.check_permissions():
568 if self.check_permissions():
569 log.debug('Permission granted for %s %s' % (cls, self.user))
569 log.debug('Permission granted for %s %s' % (cls, self.user))
570 return func(*fargs, **fkwargs)
570 return func(*fargs, **fkwargs)
571
571
572 else:
572 else:
573 log.debug('Permission denied for %s %s' % (cls, self.user))
573 log.debug('Permission denied for %s %s' % (cls, self.user))
574 anonymous = self.user.username == 'default'
574 anonymous = self.user.username == 'default'
575
575
576 if anonymous:
576 if anonymous:
577 p = url.current()
577 p = url.current()
578
578
579 import rhodecode.lib.helpers as h
579 import rhodecode.lib.helpers as h
580 h.flash(_('You need to be a signed in to '
580 h.flash(_('You need to be a signed in to '
581 'view this page'),
581 'view this page'),
582 category='warning')
582 category='warning')
583 return redirect(url('login_home', came_from=p))
583 return redirect(url('login_home', came_from=p))
584
584
585 else:
585 else:
586 # redirect with forbidden ret code
586 # redirect with forbidden ret code
587 return abort(403)
587 return abort(403)
588
588
589 def check_permissions(self):
589 def check_permissions(self):
590 """Dummy function for overriding"""
590 """Dummy function for overriding"""
591 raise Exception('You have to write this function in child class')
591 raise Exception('You have to write this function in child class')
592
592
593
593
594 class HasPermissionAllDecorator(PermsDecorator):
594 class HasPermissionAllDecorator(PermsDecorator):
595 """
595 """
596 Checks for access permission for all given predicates. All of them
596 Checks for access permission for all given predicates. All of them
597 have to be meet in order to fulfill the request
597 have to be meet in order to fulfill the request
598 """
598 """
599
599
600 def check_permissions(self):
600 def check_permissions(self):
601 if self.required_perms.issubset(self.user_perms.get('global')):
601 if self.required_perms.issubset(self.user_perms.get('global')):
602 return True
602 return True
603 return False
603 return False
604
604
605
605
606 class HasPermissionAnyDecorator(PermsDecorator):
606 class HasPermissionAnyDecorator(PermsDecorator):
607 """
607 """
608 Checks for access permission for any of given predicates. In order to
608 Checks for access permission for any of given predicates. In order to
609 fulfill the request any of predicates must be meet
609 fulfill the request any of predicates must be meet
610 """
610 """
611
611
612 def check_permissions(self):
612 def check_permissions(self):
613 if self.required_perms.intersection(self.user_perms.get('global')):
613 if self.required_perms.intersection(self.user_perms.get('global')):
614 return True
614 return True
615 return False
615 return False
616
616
617
617
618 class HasRepoPermissionAllDecorator(PermsDecorator):
618 class HasRepoPermissionAllDecorator(PermsDecorator):
619 """
619 """
620 Checks for access permission for all given predicates for specific
620 Checks for access permission for all given predicates for specific
621 repository. All of them have to be meet in order to fulfill the request
621 repository. All of them have to be meet in order to fulfill the request
622 """
622 """
623
623
624 def check_permissions(self):
624 def check_permissions(self):
625 repo_name = get_repo_slug(request)
625 repo_name = get_repo_slug(request)
626 try:
626 try:
627 user_perms = set([self.user_perms['repositories'][repo_name]])
627 user_perms = set([self.user_perms['repositories'][repo_name]])
628 except KeyError:
628 except KeyError:
629 return False
629 return False
630 if self.required_perms.issubset(user_perms):
630 if self.required_perms.issubset(user_perms):
631 return True
631 return True
632 return False
632 return False
633
633
634
634
635 class HasRepoPermissionAnyDecorator(PermsDecorator):
635 class HasRepoPermissionAnyDecorator(PermsDecorator):
636 """
636 """
637 Checks for access permission for any of given predicates for specific
637 Checks for access permission for any of given predicates for specific
638 repository. In order to fulfill the request any of predicates must be meet
638 repository. In order to fulfill the request any of predicates must be meet
639 """
639 """
640
640
641 def check_permissions(self):
641 def check_permissions(self):
642 repo_name = get_repo_slug(request)
642 repo_name = get_repo_slug(request)
643
643
644 try:
644 try:
645 user_perms = set([self.user_perms['repositories'][repo_name]])
645 user_perms = set([self.user_perms['repositories'][repo_name]])
646 except KeyError:
646 except KeyError:
647 return False
647 return False
648
648
649 if self.required_perms.intersection(user_perms):
649 if self.required_perms.intersection(user_perms):
650 return True
650 return True
651 return False
651 return False
652
652
653
653
654 class HasReposGroupPermissionAllDecorator(PermsDecorator):
654 class HasReposGroupPermissionAllDecorator(PermsDecorator):
655 """
655 """
656 Checks for access permission for all given predicates for specific
656 Checks for access permission for all given predicates for specific
657 repository. All of them have to be meet in order to fulfill the request
657 repository. All of them have to be meet in order to fulfill the request
658 """
658 """
659
659
660 def check_permissions(self):
660 def check_permissions(self):
661 group_name = get_repos_group_slug(request)
661 group_name = get_repos_group_slug(request)
662 try:
662 try:
663 user_perms = set([self.user_perms['repositories_groups'][group_name]])
663 user_perms = set([self.user_perms['repositories_groups'][group_name]])
664 except KeyError:
664 except KeyError:
665 return False
665 return False
666 if self.required_perms.issubset(user_perms):
666 if self.required_perms.issubset(user_perms):
667 return True
667 return True
668 return False
668 return False
669
669
670
670
671 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
671 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
672 """
672 """
673 Checks for access permission for any of given predicates for specific
673 Checks for access permission for any of given predicates for specific
674 repository. In order to fulfill the request any of predicates must be meet
674 repository. In order to fulfill the request any of predicates must be meet
675 """
675 """
676
676
677 def check_permissions(self):
677 def check_permissions(self):
678 group_name = get_repos_group_slug(request)
678 group_name = get_repos_group_slug(request)
679
679
680 try:
680 try:
681 user_perms = set([self.user_perms['repositories_groups'][group_name]])
681 user_perms = set([self.user_perms['repositories_groups'][group_name]])
682 except KeyError:
682 except KeyError:
683 return False
683 return False
684 if self.required_perms.intersection(user_perms):
684 if self.required_perms.intersection(user_perms):
685 return True
685 return True
686 return False
686 return False
687
687
688
688
689 #==============================================================================
689 #==============================================================================
690 # CHECK FUNCTIONS
690 # CHECK FUNCTIONS
691 #==============================================================================
691 #==============================================================================
692 class PermsFunction(object):
692 class PermsFunction(object):
693 """Base function for other check functions"""
693 """Base function for other check functions"""
694
694
695 def __init__(self, *perms):
695 def __init__(self, *perms):
696 available_perms = config['available_permissions']
696 available_perms = config['available_permissions']
697
697
698 for perm in perms:
698 for perm in perms:
699 if perm not in available_perms:
699 if perm not in available_perms:
700 raise Exception("'%s' permission is not defined" % perm)
700 raise Exception("'%s' permission is not defined" % perm)
701 self.required_perms = set(perms)
701 self.required_perms = set(perms)
702 self.user_perms = None
702 self.user_perms = None
703 self.repo_name = None
703 self.repo_name = None
704 self.group_name = None
704 self.group_name = None
705
705
706 def __call__(self, check_Location=''):
706 def __call__(self, check_Location=''):
707 user = request.user
707 user = request.user
708 cls_name = self.__class__.__name__
708 cls_name = self.__class__.__name__
709 check_scope = {
709 check_scope = {
710 'HasPermissionAll': '',
710 'HasPermissionAll': '',
711 'HasPermissionAny': '',
711 'HasPermissionAny': '',
712 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
712 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
713 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
713 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
714 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
714 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
715 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
715 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
716 }.get(cls_name, '?')
716 }.get(cls_name, '?')
717 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
717 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
718 self.required_perms, user, check_scope,
718 self.required_perms, user, check_scope,
719 check_Location or 'unspecified location')
719 check_Location or 'unspecified location')
720 if not user:
720 if not user:
721 log.debug('Empty request user')
721 log.debug('Empty request user')
722 return False
722 return False
723 self.user_perms = user.permissions
723 self.user_perms = user.permissions
724 if self.check_permissions():
724 if self.check_permissions():
725 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
725 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
726 check_Location or 'unspecified location')
726 check_Location or 'unspecified location')
727 return True
727 return True
728
728
729 else:
729 else:
730 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
730 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
731 check_Location or 'unspecified location')
731 check_Location or 'unspecified location')
732 return False
732 return False
733
733
734 def check_permissions(self):
734 def check_permissions(self):
735 """Dummy function for overriding"""
735 """Dummy function for overriding"""
736 raise Exception('You have to write this function in child class')
736 raise Exception('You have to write this function in child class')
737
737
738
738
739 class HasPermissionAll(PermsFunction):
739 class HasPermissionAll(PermsFunction):
740 def check_permissions(self):
740 def check_permissions(self):
741 if self.required_perms.issubset(self.user_perms.get('global')):
741 if self.required_perms.issubset(self.user_perms.get('global')):
742 return True
742 return True
743 return False
743 return False
744
744
745
745
746 class HasPermissionAny(PermsFunction):
746 class HasPermissionAny(PermsFunction):
747 def check_permissions(self):
747 def check_permissions(self):
748 if self.required_perms.intersection(self.user_perms.get('global')):
748 if self.required_perms.intersection(self.user_perms.get('global')):
749 return True
749 return True
750 return False
750 return False
751
751
752
752
753 class HasRepoPermissionAll(PermsFunction):
753 class HasRepoPermissionAll(PermsFunction):
754 def __call__(self, repo_name=None, check_Location=''):
754 def __call__(self, repo_name=None, check_Location=''):
755 self.repo_name = repo_name
755 self.repo_name = repo_name
756 return super(HasRepoPermissionAll, self).__call__(check_Location)
756 return super(HasRepoPermissionAll, self).__call__(check_Location)
757
757
758 def check_permissions(self):
758 def check_permissions(self):
759 if not self.repo_name:
759 if not self.repo_name:
760 self.repo_name = get_repo_slug(request)
760 self.repo_name = get_repo_slug(request)
761
761
762 try:
762 try:
763 self._user_perms = set(
763 self._user_perms = set(
764 [self.user_perms['repositories'][self.repo_name]]
764 [self.user_perms['repositories'][self.repo_name]]
765 )
765 )
766 except KeyError:
766 except KeyError:
767 return False
767 return False
768 if self.required_perms.issubset(self._user_perms):
768 if self.required_perms.issubset(self._user_perms):
769 return True
769 return True
770 return False
770 return False
771
771
772
772
773 class HasRepoPermissionAny(PermsFunction):
773 class HasRepoPermissionAny(PermsFunction):
774 def __call__(self, repo_name=None, check_Location=''):
774 def __call__(self, repo_name=None, check_Location=''):
775 self.repo_name = repo_name
775 self.repo_name = repo_name
776 return super(HasRepoPermissionAny, self).__call__(check_Location)
776 return super(HasRepoPermissionAny, self).__call__(check_Location)
777
777
778 def check_permissions(self):
778 def check_permissions(self):
779 if not self.repo_name:
779 if not self.repo_name:
780 self.repo_name = get_repo_slug(request)
780 self.repo_name = get_repo_slug(request)
781
781
782 try:
782 try:
783 self._user_perms = set(
783 self._user_perms = set(
784 [self.user_perms['repositories'][self.repo_name]]
784 [self.user_perms['repositories'][self.repo_name]]
785 )
785 )
786 except KeyError:
786 except KeyError:
787 return False
787 return False
788 if self.required_perms.intersection(self._user_perms):
788 if self.required_perms.intersection(self._user_perms):
789 return True
789 return True
790 return False
790 return False
791
791
792
792
793 class HasReposGroupPermissionAny(PermsFunction):
793 class HasReposGroupPermissionAny(PermsFunction):
794 def __call__(self, group_name=None, check_Location=''):
794 def __call__(self, group_name=None, check_Location=''):
795 self.group_name = group_name
795 self.group_name = group_name
796 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
796 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
797
797
798 def check_permissions(self):
798 def check_permissions(self):
799 try:
799 try:
800 self._user_perms = set(
800 self._user_perms = set(
801 [self.user_perms['repositories_groups'][self.group_name]]
801 [self.user_perms['repositories_groups'][self.group_name]]
802 )
802 )
803 except KeyError:
803 except KeyError:
804 return False
804 return False
805 if self.required_perms.intersection(self._user_perms):
805 if self.required_perms.intersection(self._user_perms):
806 return True
806 return True
807 return False
807 return False
808
808
809
809
810 class HasReposGroupPermissionAll(PermsFunction):
810 class HasReposGroupPermissionAll(PermsFunction):
811 def __call__(self, group_name=None, check_Location=''):
811 def __call__(self, group_name=None, check_Location=''):
812 self.group_name = group_name
812 self.group_name = group_name
813 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
813 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
814
814
815 def check_permissions(self):
815 def check_permissions(self):
816 try:
816 try:
817 self._user_perms = set(
817 self._user_perms = set(
818 [self.user_perms['repositories_groups'][self.group_name]]
818 [self.user_perms['repositories_groups'][self.group_name]]
819 )
819 )
820 except KeyError:
820 except KeyError:
821 return False
821 return False
822 if self.required_perms.issubset(self._user_perms):
822 if self.required_perms.issubset(self._user_perms):
823 return True
823 return True
824 return False
824 return False
825
825
826
826
827 #==============================================================================
827 #==============================================================================
828 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
828 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
829 #==============================================================================
829 #==============================================================================
830 class HasPermissionAnyMiddleware(object):
830 class HasPermissionAnyMiddleware(object):
831 def __init__(self, *perms):
831 def __init__(self, *perms):
832 self.required_perms = set(perms)
832 self.required_perms = set(perms)
833
833
834 def __call__(self, user, repo_name):
834 def __call__(self, user, repo_name):
835 # repo_name MUST be unicode, since we handle keys in permission
835 # repo_name MUST be unicode, since we handle keys in permission
836 # dict by unicode
836 # dict by unicode
837 repo_name = safe_unicode(repo_name)
837 repo_name = safe_unicode(repo_name)
838 usr = AuthUser(user.user_id)
838 usr = AuthUser(user.user_id)
839 try:
839 try:
840 self.user_perms = set([usr.permissions['repositories'][repo_name]])
840 self.user_perms = set([usr.permissions['repositories'][repo_name]])
841 except Exception:
841 except Exception:
842 log.error('Exception while accessing permissions %s' %
842 log.error('Exception while accessing permissions %s' %
843 traceback.format_exc())
843 traceback.format_exc())
844 self.user_perms = set()
844 self.user_perms = set()
845 self.username = user.username
845 self.username = user.username
846 self.repo_name = repo_name
846 self.repo_name = repo_name
847 return self.check_permissions()
847 return self.check_permissions()
848
848
849 def check_permissions(self):
849 def check_permissions(self):
850 log.debug('checking VCS protocol '
850 log.debug('checking VCS protocol '
851 'permissions %s for user:%s repository:%s', self.user_perms,
851 'permissions %s for user:%s repository:%s', self.user_perms,
852 self.username, self.repo_name)
852 self.username, self.repo_name)
853 if self.required_perms.intersection(self.user_perms):
853 if self.required_perms.intersection(self.user_perms):
854 log.debug('permission granted for user:%s on repo:%s' % (
854 log.debug('permission granted for user:%s on repo:%s' % (
855 self.username, self.repo_name
855 self.username, self.repo_name
856 )
856 )
857 )
857 )
858 return True
858 return True
859 log.debug('permission denied for user:%s on repo:%s' % (
859 log.debug('permission denied for user:%s on repo:%s' % (
860 self.username, self.repo_name
860 self.username, self.repo_name
861 )
861 )
862 )
862 )
863 return False
863 return False
864
864
865
865
866 #==============================================================================
867 # SPECIAL VERSION TO HANDLE API AUTH
868 #==============================================================================
869 class _BaseApiPerm(object):
870 def __init__(self, *perms):
871 self.required_perms = set(perms)
872
873 def __call__(self, check_location='unspecified', user=None, repo_name=None):
874 cls_name = self.__class__.__name__
875 check_scope = 'user:%s, repo:%s' % (user, repo_name)
876 log.debug('checking cls:%s %s %s @ %s', cls_name,
877 self.required_perms, check_scope, check_location)
878 if not user:
879 log.debug('Empty User passed into arguments')
880 return False
881
882 ## process user
883 if not isinstance(user, AuthUser):
884 user = AuthUser(user.user_id)
885
886 if self.check_permissions(user.permissions, repo_name):
887 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
888 user, check_location)
889 return True
890
891 else:
892 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
893 user, check_location)
894 return False
895
896 def check_permissions(self, perm_defs, repo_name):
897 """
898 implement in child class should return True if permissions are ok,
899 False otherwise
900
901 :param perm_defs: dict with permission definitions
902 :param repo_name: repo name
903 """
904 raise NotImplementedError()
905
906
907 class HasPermissionAllApi(_BaseApiPerm):
908 def __call__(self, user, check_location=''):
909 return super(HasPermissionAllApi, self)\
910 .__call__(check_location=check_location, user=user)
911
912 def check_permissions(self, perm_defs, repo):
913 if self.required_perms.issubset(perm_defs.get('global')):
914 return True
915 return False
916
917
918 class HasPermissionAnyApi(_BaseApiPerm):
919 def __call__(self, user, check_location=''):
920 return super(HasPermissionAnyApi, self)\
921 .__call__(check_location=check_location, user=user)
922
923 def check_permissions(self, perm_defs, repo):
924 if self.required_perms.intersection(perm_defs.get('global')):
925 return True
926 return False
927
928
929 class HasRepoPermissionAllApi(_BaseApiPerm):
930 def __call__(self, user, repo_name, check_location=''):
931 return super(HasRepoPermissionAllApi, self)\
932 .__call__(check_location=check_location, user=user,
933 repo_name=repo_name)
934
935 def check_permissions(self, perm_defs, repo_name):
936
937 try:
938 self._user_perms = set(
939 [perm_defs['repositories'][repo_name]]
940 )
941 except KeyError:
942 log.warning(traceback.format_exc())
943 return False
944 if self.required_perms.issubset(self._user_perms):
945 return True
946 return False
947
948
949 class HasRepoPermissionAnyApi(_BaseApiPerm):
950 def __call__(self, user, repo_name, check_location=''):
951 return super(HasRepoPermissionAnyApi, self)\
952 .__call__(check_location=check_location, user=user,
953 repo_name=repo_name)
954
955 def check_permissions(self, perm_defs, repo_name):
956
957 try:
958 _user_perms = set(
959 [perm_defs['repositories'][repo_name]]
960 )
961 except KeyError:
962 log.warning(traceback.format_exc())
963 return False
964 if self.required_perms.intersection(_user_perms):
965 return True
966 return False
967
968
866 def check_ip_access(source_ip, allowed_ips=None):
969 def check_ip_access(source_ip, allowed_ips=None):
867 """
970 """
868 Checks if source_ip is a subnet of any of allowed_ips.
971 Checks if source_ip is a subnet of any of allowed_ips.
869
972
870 :param source_ip:
973 :param source_ip:
871 :param allowed_ips: list of allowed ips together with mask
974 :param allowed_ips: list of allowed ips together with mask
872 """
975 """
873 from rhodecode.lib import ipaddr
976 from rhodecode.lib import ipaddr
874 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
977 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
875 if isinstance(allowed_ips, (tuple, list, set)):
978 if isinstance(allowed_ips, (tuple, list, set)):
876 for ip in allowed_ips:
979 for ip in allowed_ips:
877 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
980 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
878 return True
981 return True
879 return False
982 return False
@@ -1,985 +1,994 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2 import random
2 import random
3 import mock
3 import mock
4
4
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6 from rhodecode.lib.compat import json
6 from rhodecode.lib.compat import json
7 from rhodecode.lib.auth import AuthUser
7 from rhodecode.lib.auth import AuthUser
8 from rhodecode.model.user import UserModel
8 from rhodecode.model.user import UserModel
9 from rhodecode.model.users_group import UsersGroupModel
9 from rhodecode.model.users_group import UsersGroupModel
10 from rhodecode.model.repo import RepoModel
10 from rhodecode.model.repo import RepoModel
11 from rhodecode.model.meta import Session
11 from rhodecode.model.meta import Session
12 from rhodecode.model.scm import ScmModel
12 from rhodecode.model.scm import ScmModel
13 from rhodecode.model.db import Repository
13 from rhodecode.model.db import Repository
14
14
15 API_URL = '/_admin/api'
15 API_URL = '/_admin/api'
16
16
17
17
18 def _build_data(apikey, method, **kw):
18 def _build_data(apikey, method, **kw):
19 """
19 """
20 Builds API data with given random ID
20 Builds API data with given random ID
21
21
22 :param random_id:
22 :param random_id:
23 :type random_id:
23 :type random_id:
24 """
24 """
25 random_id = random.randrange(1, 9999)
25 random_id = random.randrange(1, 9999)
26 return random_id, json.dumps({
26 return random_id, json.dumps({
27 "id": random_id,
27 "id": random_id,
28 "api_key": apikey,
28 "api_key": apikey,
29 "method": method,
29 "method": method,
30 "args": kw
30 "args": kw
31 })
31 })
32
32
33 jsonify = lambda obj: json.loads(json.dumps(obj))
33 jsonify = lambda obj: json.loads(json.dumps(obj))
34
34
35
35
36 def crash(*args, **kwargs):
36 def crash(*args, **kwargs):
37 raise Exception('Total Crash !')
37 raise Exception('Total Crash !')
38
38
39
39
40 def api_call(test_obj, params):
40 def api_call(test_obj, params):
41 response = test_obj.app.post(API_URL, content_type='application/json',
41 response = test_obj.app.post(API_URL, content_type='application/json',
42 params=params)
42 params=params)
43 return response
43 return response
44
44
45
45
46 TEST_USERS_GROUP = 'test_users_group'
46 TEST_USERS_GROUP = 'test_users_group'
47
47
48
48
49 def make_users_group(name=TEST_USERS_GROUP):
49 def make_users_group(name=TEST_USERS_GROUP):
50 gr = UsersGroupModel().create(name=name)
50 gr = UsersGroupModel().create(name=name)
51 UsersGroupModel().add_user_to_group(users_group=gr,
51 UsersGroupModel().add_user_to_group(users_group=gr,
52 user=TEST_USER_ADMIN_LOGIN)
52 user=TEST_USER_ADMIN_LOGIN)
53 Session().commit()
53 Session().commit()
54 return gr
54 return gr
55
55
56
56
57 def destroy_users_group(name=TEST_USERS_GROUP):
57 def destroy_users_group(name=TEST_USERS_GROUP):
58 UsersGroupModel().delete(users_group=name, force=True)
58 UsersGroupModel().delete(users_group=name, force=True)
59 Session().commit()
59 Session().commit()
60
60
61
61
62 def create_repo(repo_name, repo_type):
62 def create_repo(repo_name, repo_type):
63 # create new repo
63 # create new repo
64 form_data = _get_repo_create_params(
64 form_data = _get_repo_create_params(
65 repo_name_full=repo_name,
65 repo_name_full=repo_name,
66 repo_description='description %s' % repo_name,
66 repo_description='description %s' % repo_name,
67 )
67 )
68 cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
68 cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
69 r = RepoModel().create(form_data, cur_user)
69 r = RepoModel().create(form_data, cur_user)
70 Session().commit()
70 Session().commit()
71 return r
71 return r
72
72
73
73
74 def create_fork(fork_name, fork_type, fork_of):
74 def create_fork(fork_name, fork_type, fork_of):
75 fork = RepoModel(Session())._get_repo(fork_of)
75 fork = RepoModel(Session())._get_repo(fork_of)
76 r = create_repo(fork_name, fork_type)
76 r = create_repo(fork_name, fork_type)
77 r.fork = fork
77 r.fork = fork
78 Session().add(r)
78 Session().add(r)
79 Session().commit()
79 Session().commit()
80 return r
80 return r
81
81
82
82
83 def destroy_repo(repo_name):
83 def destroy_repo(repo_name):
84 RepoModel().delete(repo_name)
84 RepoModel().delete(repo_name)
85 Session().commit()
85 Session().commit()
86
86
87
87
88 class BaseTestApi(object):
88 class BaseTestApi(object):
89 REPO = None
89 REPO = None
90 REPO_TYPE = None
90 REPO_TYPE = None
91
91
92 @classmethod
92 @classmethod
93 def setUpClass(self):
93 def setUpClass(self):
94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
95 self.apikey = self.usr.api_key
95 self.apikey = self.usr.api_key
96 self.TEST_USER = UserModel().create_or_update(
96 self.TEST_USER = UserModel().create_or_update(
97 username='test-api',
97 username='test-api',
98 password='test',
98 password='test',
99 email='test@api.rhodecode.org',
99 email='test@api.rhodecode.org',
100 firstname='first',
100 firstname='first',
101 lastname='last'
101 lastname='last'
102 )
102 )
103 Session().commit()
103 Session().commit()
104 self.TEST_USER_LOGIN = self.TEST_USER.username
104 self.TEST_USER_LOGIN = self.TEST_USER.username
105
105
106 @classmethod
106 @classmethod
107 def teardownClass(self):
107 def teardownClass(self):
108 pass
108 pass
109
109
110 def setUp(self):
110 def setUp(self):
111 self.maxDiff = None
111 self.maxDiff = None
112 make_users_group()
112 make_users_group()
113
113
114 def tearDown(self):
114 def tearDown(self):
115 destroy_users_group()
115 destroy_users_group()
116
116
117 def _compare_ok(self, id_, expected, given):
117 def _compare_ok(self, id_, expected, given):
118 expected = jsonify({
118 expected = jsonify({
119 'id': id_,
119 'id': id_,
120 'error': None,
120 'error': None,
121 'result': expected
121 'result': expected
122 })
122 })
123 given = json.loads(given)
123 given = json.loads(given)
124 self.assertEqual(expected, given)
124 self.assertEqual(expected, given)
125
125
126 def _compare_error(self, id_, expected, given):
126 def _compare_error(self, id_, expected, given):
127 expected = jsonify({
127 expected = jsonify({
128 'id': id_,
128 'id': id_,
129 'error': expected,
129 'error': expected,
130 'result': None
130 'result': None
131 })
131 })
132 given = json.loads(given)
132 given = json.loads(given)
133 self.assertEqual(expected, given)
133 self.assertEqual(expected, given)
134
134
135 # def test_Optional(self):
135 # def test_Optional(self):
136 # from rhodecode.controllers.api.api import Optional
136 # from rhodecode.controllers.api.api import Optional
137 # option1 = Optional(None)
137 # option1 = Optional(None)
138 # self.assertEqual('<Optional:%s>' % None, repr(option1))
138 # self.assertEqual('<Optional:%s>' % None, repr(option1))
139 #
139 #
140 # self.assertEqual(1, Optional.extract(Optional(1)))
140 # self.assertEqual(1, Optional.extract(Optional(1)))
141 # self.assertEqual('trololo', Optional.extract('trololo'))
141 # self.assertEqual('trololo', Optional.extract('trololo'))
142
142
143 def test_api_wrong_key(self):
143 def test_api_wrong_key(self):
144 id_, params = _build_data('trololo', 'get_user')
144 id_, params = _build_data('trololo', 'get_user')
145 response = api_call(self, params)
145 response = api_call(self, params)
146
146
147 expected = 'Invalid API KEY'
147 expected = 'Invalid API KEY'
148 self._compare_error(id_, expected, given=response.body)
148 self._compare_error(id_, expected, given=response.body)
149
149
150 def test_api_missing_non_optional_param(self):
150 def test_api_missing_non_optional_param(self):
151 id_, params = _build_data(self.apikey, 'get_user')
151 id_, params = _build_data(self.apikey, 'get_user')
152 response = api_call(self, params)
152 response = api_call(self, params)
153
153
154 expected = 'Missing non optional `userid` arg in JSON DATA'
154 expected = 'Missing non optional `userid` arg in JSON DATA'
155 self._compare_error(id_, expected, given=response.body)
155 self._compare_error(id_, expected, given=response.body)
156
156
157 def test_api_get_users(self):
157 def test_api_get_users(self):
158 id_, params = _build_data(self.apikey, 'get_users',)
158 id_, params = _build_data(self.apikey, 'get_users',)
159 response = api_call(self, params)
159 response = api_call(self, params)
160 ret_all = []
160 ret_all = []
161 for usr in UserModel().get_all():
161 for usr in UserModel().get_all():
162 ret = usr.get_api_data()
162 ret = usr.get_api_data()
163 ret_all.append(jsonify(ret))
163 ret_all.append(jsonify(ret))
164 expected = ret_all
164 expected = ret_all
165 self._compare_ok(id_, expected, given=response.body)
165 self._compare_ok(id_, expected, given=response.body)
166
166
167 def test_api_get_user(self):
167 def test_api_get_user(self):
168 id_, params = _build_data(self.apikey, 'get_user',
168 id_, params = _build_data(self.apikey, 'get_user',
169 userid=TEST_USER_ADMIN_LOGIN)
169 userid=TEST_USER_ADMIN_LOGIN)
170 response = api_call(self, params)
170 response = api_call(self, params)
171
171
172 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
172 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
173 ret = usr.get_api_data()
173 ret = usr.get_api_data()
174 ret['permissions'] = AuthUser(usr.user_id).permissions
174 ret['permissions'] = AuthUser(usr.user_id).permissions
175
175
176 expected = ret
176 expected = ret
177 self._compare_ok(id_, expected, given=response.body)
177 self._compare_ok(id_, expected, given=response.body)
178
178
179 def test_api_get_user_that_does_not_exist(self):
179 def test_api_get_user_that_does_not_exist(self):
180 id_, params = _build_data(self.apikey, 'get_user',
180 id_, params = _build_data(self.apikey, 'get_user',
181 userid='trololo')
181 userid='trololo')
182 response = api_call(self, params)
182 response = api_call(self, params)
183
183
184 expected = "user `%s` does not exist" % 'trololo'
184 expected = "user `%s` does not exist" % 'trololo'
185 self._compare_error(id_, expected, given=response.body)
185 self._compare_error(id_, expected, given=response.body)
186
186
187 def test_api_pull(self):
187 def test_api_pull(self):
188 #TODO: issues with rhodecode_extras here.. not sure why !
188 #TODO: issues with rhodecode_extras here.. not sure why !
189 pass
189 pass
190
190
191 # repo_name = 'test_pull'
191 # repo_name = 'test_pull'
192 # r = create_repo(repo_name, self.REPO_TYPE)
192 # r = create_repo(repo_name, self.REPO_TYPE)
193 # r.clone_uri = TEST_self.REPO
193 # r.clone_uri = TEST_self.REPO
194 # Session.add(r)
194 # Session.add(r)
195 # Session.commit()
195 # Session.commit()
196 #
196 #
197 # id_, params = _build_data(self.apikey, 'pull',
197 # id_, params = _build_data(self.apikey, 'pull',
198 # repoid=repo_name,)
198 # repoid=repo_name,)
199 # response = self.app.post(API_URL, content_type='application/json',
199 # response = self.app.post(API_URL, content_type='application/json',
200 # params=params)
200 # params=params)
201 #
201 #
202 # expected = 'Pulled from `%s`' % repo_name
202 # expected = 'Pulled from `%s`' % repo_name
203 # self._compare_ok(id_, expected, given=response.body)
203 # self._compare_ok(id_, expected, given=response.body)
204 #
204 #
205 # destroy_repo(repo_name)
205 # destroy_repo(repo_name)
206
206
207 def test_api_pull_error(self):
207 def test_api_pull_error(self):
208 id_, params = _build_data(self.apikey, 'pull',
208 id_, params = _build_data(self.apikey, 'pull',
209 repoid=self.REPO,)
209 repoid=self.REPO,)
210 response = api_call(self, params)
210 response = api_call(self, params)
211
211
212 expected = 'Unable to pull changes from `%s`' % self.REPO
212 expected = 'Unable to pull changes from `%s`' % self.REPO
213 self._compare_error(id_, expected, given=response.body)
213 self._compare_error(id_, expected, given=response.body)
214
214
215 def test_api_rescan_repos(self):
215 def test_api_rescan_repos(self):
216 id_, params = _build_data(self.apikey, 'rescan_repos')
216 id_, params = _build_data(self.apikey, 'rescan_repos')
217 response = api_call(self, params)
217 response = api_call(self, params)
218
218
219 expected = {'added': [], 'removed': []}
219 expected = {'added': [], 'removed': []}
220 self._compare_ok(id_, expected, given=response.body)
220 self._compare_ok(id_, expected, given=response.body)
221
221
222 @mock.patch.object(ScmModel, 'repo_scan', crash)
222 @mock.patch.object(ScmModel, 'repo_scan', crash)
223 def test_api_rescann_error(self):
223 def test_api_rescann_error(self):
224 id_, params = _build_data(self.apikey, 'rescan_repos',)
224 id_, params = _build_data(self.apikey, 'rescan_repos',)
225 response = api_call(self, params)
225 response = api_call(self, params)
226
226
227 expected = 'Error occurred during rescan repositories action'
227 expected = 'Error occurred during rescan repositories action'
228 self._compare_error(id_, expected, given=response.body)
228 self._compare_error(id_, expected, given=response.body)
229
229
230 def test_api_lock_repo_lock_aquire(self):
230 def test_api_lock_repo_lock_aquire(self):
231 id_, params = _build_data(self.apikey, 'lock',
231 id_, params = _build_data(self.apikey, 'lock',
232 userid=TEST_USER_ADMIN_LOGIN,
232 userid=TEST_USER_ADMIN_LOGIN,
233 repoid=self.REPO,
233 repoid=self.REPO,
234 locked=True)
234 locked=True)
235 response = api_call(self, params)
235 response = api_call(self, params)
236 expected = ('User `%s` set lock state for repo `%s` to `%s`'
236 expected = ('User `%s` set lock state for repo `%s` to `%s`'
237 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
237 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
238 self._compare_ok(id_, expected, given=response.body)
238 self._compare_ok(id_, expected, given=response.body)
239
239
240 def test_api_lock_repo_lock_release(self):
240 def test_api_lock_repo_lock_release(self):
241 id_, params = _build_data(self.apikey, 'lock',
241 id_, params = _build_data(self.apikey, 'lock',
242 userid=TEST_USER_ADMIN_LOGIN,
242 userid=TEST_USER_ADMIN_LOGIN,
243 repoid=self.REPO,
243 repoid=self.REPO,
244 locked=False)
244 locked=False)
245 response = api_call(self, params)
245 response = api_call(self, params)
246 expected = ('User `%s` set lock state for repo `%s` to `%s`'
246 expected = ('User `%s` set lock state for repo `%s` to `%s`'
247 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
247 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
248 self._compare_ok(id_, expected, given=response.body)
248 self._compare_ok(id_, expected, given=response.body)
249
249
250 def test_api_lock_repo_lock_aquire_optional_userid(self):
251 id_, params = _build_data(self.apikey, 'lock',
252 repoid=self.REPO,
253 locked=True)
254 response = api_call(self, params)
255 expected = ('User `%s` set lock state for repo `%s` to `%s`'
256 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
257 self._compare_ok(id_, expected, given=response.body)
258
250 @mock.patch.object(Repository, 'lock', crash)
259 @mock.patch.object(Repository, 'lock', crash)
251 def test_api_lock_error(self):
260 def test_api_lock_error(self):
252 id_, params = _build_data(self.apikey, 'lock',
261 id_, params = _build_data(self.apikey, 'lock',
253 userid=TEST_USER_ADMIN_LOGIN,
262 userid=TEST_USER_ADMIN_LOGIN,
254 repoid=self.REPO,
263 repoid=self.REPO,
255 locked=True)
264 locked=True)
256 response = api_call(self, params)
265 response = api_call(self, params)
257
266
258 expected = 'Error occurred locking repository `%s`' % self.REPO
267 expected = 'Error occurred locking repository `%s`' % self.REPO
259 self._compare_error(id_, expected, given=response.body)
268 self._compare_error(id_, expected, given=response.body)
260
269
261 def test_api_create_existing_user(self):
270 def test_api_create_existing_user(self):
262 id_, params = _build_data(self.apikey, 'create_user',
271 id_, params = _build_data(self.apikey, 'create_user',
263 username=TEST_USER_ADMIN_LOGIN,
272 username=TEST_USER_ADMIN_LOGIN,
264 email='test@foo.com',
273 email='test@foo.com',
265 password='trololo')
274 password='trololo')
266 response = api_call(self, params)
275 response = api_call(self, params)
267
276
268 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
277 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
269 self._compare_error(id_, expected, given=response.body)
278 self._compare_error(id_, expected, given=response.body)
270
279
271 def test_api_create_user_with_existing_email(self):
280 def test_api_create_user_with_existing_email(self):
272 id_, params = _build_data(self.apikey, 'create_user',
281 id_, params = _build_data(self.apikey, 'create_user',
273 username=TEST_USER_ADMIN_LOGIN + 'new',
282 username=TEST_USER_ADMIN_LOGIN + 'new',
274 email=TEST_USER_REGULAR_EMAIL,
283 email=TEST_USER_REGULAR_EMAIL,
275 password='trololo')
284 password='trololo')
276 response = api_call(self, params)
285 response = api_call(self, params)
277
286
278 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
287 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
279 self._compare_error(id_, expected, given=response.body)
288 self._compare_error(id_, expected, given=response.body)
280
289
281 def test_api_create_user(self):
290 def test_api_create_user(self):
282 username = 'test_new_api_user'
291 username = 'test_new_api_user'
283 email = username + "@foo.com"
292 email = username + "@foo.com"
284
293
285 id_, params = _build_data(self.apikey, 'create_user',
294 id_, params = _build_data(self.apikey, 'create_user',
286 username=username,
295 username=username,
287 email=email,
296 email=email,
288 password='trololo')
297 password='trololo')
289 response = api_call(self, params)
298 response = api_call(self, params)
290
299
291 usr = UserModel().get_by_username(username)
300 usr = UserModel().get_by_username(username)
292 ret = dict(
301 ret = dict(
293 msg='created new user `%s`' % username,
302 msg='created new user `%s`' % username,
294 user=jsonify(usr.get_api_data())
303 user=jsonify(usr.get_api_data())
295 )
304 )
296
305
297 expected = ret
306 expected = ret
298 self._compare_ok(id_, expected, given=response.body)
307 self._compare_ok(id_, expected, given=response.body)
299
308
300 UserModel().delete(usr.user_id)
309 UserModel().delete(usr.user_id)
301 Session().commit()
310 Session().commit()
302
311
303 @mock.patch.object(UserModel, 'create_or_update', crash)
312 @mock.patch.object(UserModel, 'create_or_update', crash)
304 def test_api_create_user_when_exception_happened(self):
313 def test_api_create_user_when_exception_happened(self):
305
314
306 username = 'test_new_api_user'
315 username = 'test_new_api_user'
307 email = username + "@foo.com"
316 email = username + "@foo.com"
308
317
309 id_, params = _build_data(self.apikey, 'create_user',
318 id_, params = _build_data(self.apikey, 'create_user',
310 username=username,
319 username=username,
311 email=email,
320 email=email,
312 password='trololo')
321 password='trololo')
313 response = api_call(self, params)
322 response = api_call(self, params)
314 expected = 'failed to create user `%s`' % username
323 expected = 'failed to create user `%s`' % username
315 self._compare_error(id_, expected, given=response.body)
324 self._compare_error(id_, expected, given=response.body)
316
325
317 def test_api_delete_user(self):
326 def test_api_delete_user(self):
318 usr = UserModel().create_or_update(username=u'test_user',
327 usr = UserModel().create_or_update(username=u'test_user',
319 password=u'qweqwe',
328 password=u'qweqwe',
320 email=u'u232@rhodecode.org',
329 email=u'u232@rhodecode.org',
321 firstname=u'u1', lastname=u'u1')
330 firstname=u'u1', lastname=u'u1')
322 Session().commit()
331 Session().commit()
323 username = usr.username
332 username = usr.username
324 email = usr.email
333 email = usr.email
325 usr_id = usr.user_id
334 usr_id = usr.user_id
326 ## DELETE THIS USER NOW
335 ## DELETE THIS USER NOW
327
336
328 id_, params = _build_data(self.apikey, 'delete_user',
337 id_, params = _build_data(self.apikey, 'delete_user',
329 userid=username,)
338 userid=username,)
330 response = api_call(self, params)
339 response = api_call(self, params)
331
340
332 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
341 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
333 'user': None}
342 'user': None}
334 expected = ret
343 expected = ret
335 self._compare_ok(id_, expected, given=response.body)
344 self._compare_ok(id_, expected, given=response.body)
336
345
337 @mock.patch.object(UserModel, 'delete', crash)
346 @mock.patch.object(UserModel, 'delete', crash)
338 def test_api_delete_user_when_exception_happened(self):
347 def test_api_delete_user_when_exception_happened(self):
339 usr = UserModel().create_or_update(username=u'test_user',
348 usr = UserModel().create_or_update(username=u'test_user',
340 password=u'qweqwe',
349 password=u'qweqwe',
341 email=u'u232@rhodecode.org',
350 email=u'u232@rhodecode.org',
342 firstname=u'u1', lastname=u'u1')
351 firstname=u'u1', lastname=u'u1')
343 Session().commit()
352 Session().commit()
344 username = usr.username
353 username = usr.username
345
354
346 id_, params = _build_data(self.apikey, 'delete_user',
355 id_, params = _build_data(self.apikey, 'delete_user',
347 userid=username,)
356 userid=username,)
348 response = api_call(self, params)
357 response = api_call(self, params)
349 ret = 'failed to delete ID:%s %s' % (usr.user_id,
358 ret = 'failed to delete ID:%s %s' % (usr.user_id,
350 usr.username)
359 usr.username)
351 expected = ret
360 expected = ret
352 self._compare_error(id_, expected, given=response.body)
361 self._compare_error(id_, expected, given=response.body)
353
362
354 @parameterized.expand([('firstname', 'new_username'),
363 @parameterized.expand([('firstname', 'new_username'),
355 ('lastname', 'new_username'),
364 ('lastname', 'new_username'),
356 ('email', 'new_username'),
365 ('email', 'new_username'),
357 ('admin', True),
366 ('admin', True),
358 ('admin', False),
367 ('admin', False),
359 ('ldap_dn', 'test'),
368 ('ldap_dn', 'test'),
360 ('ldap_dn', None),
369 ('ldap_dn', None),
361 ('active', False),
370 ('active', False),
362 ('active', True),
371 ('active', True),
363 ('password', 'newpass')
372 ('password', 'newpass')
364 ])
373 ])
365 def test_api_update_user(self, name, expected):
374 def test_api_update_user(self, name, expected):
366 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
375 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
367 kw = {name: expected,
376 kw = {name: expected,
368 'userid': usr.user_id}
377 'userid': usr.user_id}
369 id_, params = _build_data(self.apikey, 'update_user', **kw)
378 id_, params = _build_data(self.apikey, 'update_user', **kw)
370 response = api_call(self, params)
379 response = api_call(self, params)
371
380
372 ret = {
381 ret = {
373 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
382 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
374 'user': jsonify(UserModel()\
383 'user': jsonify(UserModel()\
375 .get_by_username(self.TEST_USER_LOGIN)\
384 .get_by_username(self.TEST_USER_LOGIN)\
376 .get_api_data())
385 .get_api_data())
377 }
386 }
378
387
379 expected = ret
388 expected = ret
380 self._compare_ok(id_, expected, given=response.body)
389 self._compare_ok(id_, expected, given=response.body)
381
390
382 def test_api_update_user_no_changed_params(self):
391 def test_api_update_user_no_changed_params(self):
383 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
392 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
384 ret = jsonify(usr.get_api_data())
393 ret = jsonify(usr.get_api_data())
385 id_, params = _build_data(self.apikey, 'update_user',
394 id_, params = _build_data(self.apikey, 'update_user',
386 userid=TEST_USER_ADMIN_LOGIN)
395 userid=TEST_USER_ADMIN_LOGIN)
387
396
388 response = api_call(self, params)
397 response = api_call(self, params)
389 ret = {
398 ret = {
390 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
399 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
391 'user': ret
400 'user': ret
392 }
401 }
393 expected = ret
402 expected = ret
394 self._compare_ok(id_, expected, given=response.body)
403 self._compare_ok(id_, expected, given=response.body)
395
404
396 def test_api_update_user_by_user_id(self):
405 def test_api_update_user_by_user_id(self):
397 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
406 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
398 ret = jsonify(usr.get_api_data())
407 ret = jsonify(usr.get_api_data())
399 id_, params = _build_data(self.apikey, 'update_user',
408 id_, params = _build_data(self.apikey, 'update_user',
400 userid=usr.user_id)
409 userid=usr.user_id)
401
410
402 response = api_call(self, params)
411 response = api_call(self, params)
403 ret = {
412 ret = {
404 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
413 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
405 'user': ret
414 'user': ret
406 }
415 }
407 expected = ret
416 expected = ret
408 self._compare_ok(id_, expected, given=response.body)
417 self._compare_ok(id_, expected, given=response.body)
409
418
410 @mock.patch.object(UserModel, 'update_user', crash)
419 @mock.patch.object(UserModel, 'update_user', crash)
411 def test_api_update_user_when_exception_happens(self):
420 def test_api_update_user_when_exception_happens(self):
412 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
421 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
413 ret = jsonify(usr.get_api_data())
422 ret = jsonify(usr.get_api_data())
414 id_, params = _build_data(self.apikey, 'update_user',
423 id_, params = _build_data(self.apikey, 'update_user',
415 userid=usr.user_id)
424 userid=usr.user_id)
416
425
417 response = api_call(self, params)
426 response = api_call(self, params)
418 ret = 'failed to update user `%s`' % usr.user_id
427 ret = 'failed to update user `%s`' % usr.user_id
419
428
420 expected = ret
429 expected = ret
421 self._compare_error(id_, expected, given=response.body)
430 self._compare_error(id_, expected, given=response.body)
422
431
423 def test_api_get_repo(self):
432 def test_api_get_repo(self):
424 new_group = 'some_new_group'
433 new_group = 'some_new_group'
425 make_users_group(new_group)
434 make_users_group(new_group)
426 RepoModel().grant_users_group_permission(repo=self.REPO,
435 RepoModel().grant_users_group_permission(repo=self.REPO,
427 group_name=new_group,
436 group_name=new_group,
428 perm='repository.read')
437 perm='repository.read')
429 Session().commit()
438 Session().commit()
430 id_, params = _build_data(self.apikey, 'get_repo',
439 id_, params = _build_data(self.apikey, 'get_repo',
431 repoid=self.REPO)
440 repoid=self.REPO)
432 response = api_call(self, params)
441 response = api_call(self, params)
433
442
434 repo = RepoModel().get_by_repo_name(self.REPO)
443 repo = RepoModel().get_by_repo_name(self.REPO)
435 ret = repo.get_api_data()
444 ret = repo.get_api_data()
436
445
437 members = []
446 members = []
438 for user in repo.repo_to_perm:
447 for user in repo.repo_to_perm:
439 perm = user.permission.permission_name
448 perm = user.permission.permission_name
440 user = user.user
449 user = user.user
441 user_data = user.get_api_data()
450 user_data = user.get_api_data()
442 user_data['type'] = "user"
451 user_data['type'] = "user"
443 user_data['permission'] = perm
452 user_data['permission'] = perm
444 members.append(user_data)
453 members.append(user_data)
445
454
446 for users_group in repo.users_group_to_perm:
455 for users_group in repo.users_group_to_perm:
447 perm = users_group.permission.permission_name
456 perm = users_group.permission.permission_name
448 users_group = users_group.users_group
457 users_group = users_group.users_group
449 users_group_data = users_group.get_api_data()
458 users_group_data = users_group.get_api_data()
450 users_group_data['type'] = "users_group"
459 users_group_data['type'] = "users_group"
451 users_group_data['permission'] = perm
460 users_group_data['permission'] = perm
452 members.append(users_group_data)
461 members.append(users_group_data)
453
462
454 ret['members'] = members
463 ret['members'] = members
455
464
456 expected = ret
465 expected = ret
457 self._compare_ok(id_, expected, given=response.body)
466 self._compare_ok(id_, expected, given=response.body)
458 destroy_users_group(new_group)
467 destroy_users_group(new_group)
459
468
460 def test_api_get_repo_that_doesn_not_exist(self):
469 def test_api_get_repo_that_doesn_not_exist(self):
461 id_, params = _build_data(self.apikey, 'get_repo',
470 id_, params = _build_data(self.apikey, 'get_repo',
462 repoid='no-such-repo')
471 repoid='no-such-repo')
463 response = api_call(self, params)
472 response = api_call(self, params)
464
473
465 ret = 'repository `%s` does not exist' % 'no-such-repo'
474 ret = 'repository `%s` does not exist' % 'no-such-repo'
466 expected = ret
475 expected = ret
467 self._compare_error(id_, expected, given=response.body)
476 self._compare_error(id_, expected, given=response.body)
468
477
469 def test_api_get_repos(self):
478 def test_api_get_repos(self):
470 id_, params = _build_data(self.apikey, 'get_repos')
479 id_, params = _build_data(self.apikey, 'get_repos')
471 response = api_call(self, params)
480 response = api_call(self, params)
472
481
473 result = []
482 result = []
474 for repo in RepoModel().get_all():
483 for repo in RepoModel().get_all():
475 result.append(repo.get_api_data())
484 result.append(repo.get_api_data())
476 ret = jsonify(result)
485 ret = jsonify(result)
477
486
478 expected = ret
487 expected = ret
479 self._compare_ok(id_, expected, given=response.body)
488 self._compare_ok(id_, expected, given=response.body)
480
489
481 @parameterized.expand([('all', 'all'),
490 @parameterized.expand([('all', 'all'),
482 ('dirs', 'dirs'),
491 ('dirs', 'dirs'),
483 ('files', 'files'), ])
492 ('files', 'files'), ])
484 def test_api_get_repo_nodes(self, name, ret_type):
493 def test_api_get_repo_nodes(self, name, ret_type):
485 rev = 'tip'
494 rev = 'tip'
486 path = '/'
495 path = '/'
487 id_, params = _build_data(self.apikey, 'get_repo_nodes',
496 id_, params = _build_data(self.apikey, 'get_repo_nodes',
488 repoid=self.REPO, revision=rev,
497 repoid=self.REPO, revision=rev,
489 root_path=path,
498 root_path=path,
490 ret_type=ret_type)
499 ret_type=ret_type)
491 response = api_call(self, params)
500 response = api_call(self, params)
492
501
493 # we don't the actual return types here since it's tested somewhere
502 # we don't the actual return types here since it's tested somewhere
494 # else
503 # else
495 expected = json.loads(response.body)['result']
504 expected = json.loads(response.body)['result']
496 self._compare_ok(id_, expected, given=response.body)
505 self._compare_ok(id_, expected, given=response.body)
497
506
498 def test_api_get_repo_nodes_bad_revisions(self):
507 def test_api_get_repo_nodes_bad_revisions(self):
499 rev = 'i-dont-exist'
508 rev = 'i-dont-exist'
500 path = '/'
509 path = '/'
501 id_, params = _build_data(self.apikey, 'get_repo_nodes',
510 id_, params = _build_data(self.apikey, 'get_repo_nodes',
502 repoid=self.REPO, revision=rev,
511 repoid=self.REPO, revision=rev,
503 root_path=path,)
512 root_path=path,)
504 response = api_call(self, params)
513 response = api_call(self, params)
505
514
506 expected = 'failed to get repo: `%s` nodes' % self.REPO
515 expected = 'failed to get repo: `%s` nodes' % self.REPO
507 self._compare_error(id_, expected, given=response.body)
516 self._compare_error(id_, expected, given=response.body)
508
517
509 def test_api_get_repo_nodes_bad_path(self):
518 def test_api_get_repo_nodes_bad_path(self):
510 rev = 'tip'
519 rev = 'tip'
511 path = '/idontexits'
520 path = '/idontexits'
512 id_, params = _build_data(self.apikey, 'get_repo_nodes',
521 id_, params = _build_data(self.apikey, 'get_repo_nodes',
513 repoid=self.REPO, revision=rev,
522 repoid=self.REPO, revision=rev,
514 root_path=path,)
523 root_path=path,)
515 response = api_call(self, params)
524 response = api_call(self, params)
516
525
517 expected = 'failed to get repo: `%s` nodes' % self.REPO
526 expected = 'failed to get repo: `%s` nodes' % self.REPO
518 self._compare_error(id_, expected, given=response.body)
527 self._compare_error(id_, expected, given=response.body)
519
528
520 def test_api_get_repo_nodes_bad_ret_type(self):
529 def test_api_get_repo_nodes_bad_ret_type(self):
521 rev = 'tip'
530 rev = 'tip'
522 path = '/'
531 path = '/'
523 ret_type = 'error'
532 ret_type = 'error'
524 id_, params = _build_data(self.apikey, 'get_repo_nodes',
533 id_, params = _build_data(self.apikey, 'get_repo_nodes',
525 repoid=self.REPO, revision=rev,
534 repoid=self.REPO, revision=rev,
526 root_path=path,
535 root_path=path,
527 ret_type=ret_type)
536 ret_type=ret_type)
528 response = api_call(self, params)
537 response = api_call(self, params)
529
538
530 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
539 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
531 self._compare_error(id_, expected, given=response.body)
540 self._compare_error(id_, expected, given=response.body)
532
541
533 def test_api_create_repo(self):
542 def test_api_create_repo(self):
534 repo_name = 'api-repo'
543 repo_name = 'api-repo'
535 id_, params = _build_data(self.apikey, 'create_repo',
544 id_, params = _build_data(self.apikey, 'create_repo',
536 repo_name=repo_name,
545 repo_name=repo_name,
537 owner=TEST_USER_ADMIN_LOGIN,
546 owner=TEST_USER_ADMIN_LOGIN,
538 repo_type='hg',
547 repo_type='hg',
539 )
548 )
540 response = api_call(self, params)
549 response = api_call(self, params)
541
550
542 repo = RepoModel().get_by_repo_name(repo_name)
551 repo = RepoModel().get_by_repo_name(repo_name)
543 ret = {
552 ret = {
544 'msg': 'Created new repository `%s`' % repo_name,
553 'msg': 'Created new repository `%s`' % repo_name,
545 'repo': jsonify(repo.get_api_data())
554 'repo': jsonify(repo.get_api_data())
546 }
555 }
547 expected = ret
556 expected = ret
548 self._compare_ok(id_, expected, given=response.body)
557 self._compare_ok(id_, expected, given=response.body)
549 destroy_repo(repo_name)
558 destroy_repo(repo_name)
550
559
551 def test_api_create_repo_unknown_owner(self):
560 def test_api_create_repo_unknown_owner(self):
552 repo_name = 'api-repo'
561 repo_name = 'api-repo'
553 owner = 'i-dont-exist'
562 owner = 'i-dont-exist'
554 id_, params = _build_data(self.apikey, 'create_repo',
563 id_, params = _build_data(self.apikey, 'create_repo',
555 repo_name=repo_name,
564 repo_name=repo_name,
556 owner=owner,
565 owner=owner,
557 repo_type='hg',
566 repo_type='hg',
558 )
567 )
559 response = api_call(self, params)
568 response = api_call(self, params)
560 expected = 'user `%s` does not exist' % owner
569 expected = 'user `%s` does not exist' % owner
561 self._compare_error(id_, expected, given=response.body)
570 self._compare_error(id_, expected, given=response.body)
562
571
563 def test_api_create_repo_exists(self):
572 def test_api_create_repo_exists(self):
564 repo_name = self.REPO
573 repo_name = self.REPO
565 id_, params = _build_data(self.apikey, 'create_repo',
574 id_, params = _build_data(self.apikey, 'create_repo',
566 repo_name=repo_name,
575 repo_name=repo_name,
567 owner=TEST_USER_ADMIN_LOGIN,
576 owner=TEST_USER_ADMIN_LOGIN,
568 repo_type='hg',
577 repo_type='hg',
569 )
578 )
570 response = api_call(self, params)
579 response = api_call(self, params)
571 expected = "repo `%s` already exist" % repo_name
580 expected = "repo `%s` already exist" % repo_name
572 self._compare_error(id_, expected, given=response.body)
581 self._compare_error(id_, expected, given=response.body)
573
582
574 @mock.patch.object(RepoModel, 'create_repo', crash)
583 @mock.patch.object(RepoModel, 'create_repo', crash)
575 def test_api_create_repo_exception_occurred(self):
584 def test_api_create_repo_exception_occurred(self):
576 repo_name = 'api-repo'
585 repo_name = 'api-repo'
577 id_, params = _build_data(self.apikey, 'create_repo',
586 id_, params = _build_data(self.apikey, 'create_repo',
578 repo_name=repo_name,
587 repo_name=repo_name,
579 owner=TEST_USER_ADMIN_LOGIN,
588 owner=TEST_USER_ADMIN_LOGIN,
580 repo_type='hg',
589 repo_type='hg',
581 )
590 )
582 response = api_call(self, params)
591 response = api_call(self, params)
583 expected = 'failed to create repository `%s`' % repo_name
592 expected = 'failed to create repository `%s`' % repo_name
584 self._compare_error(id_, expected, given=response.body)
593 self._compare_error(id_, expected, given=response.body)
585
594
586 def test_api_delete_repo(self):
595 def test_api_delete_repo(self):
587 repo_name = 'api_delete_me'
596 repo_name = 'api_delete_me'
588 create_repo(repo_name, self.REPO_TYPE)
597 create_repo(repo_name, self.REPO_TYPE)
589
598
590 id_, params = _build_data(self.apikey, 'delete_repo',
599 id_, params = _build_data(self.apikey, 'delete_repo',
591 repoid=repo_name,)
600 repoid=repo_name,)
592 response = api_call(self, params)
601 response = api_call(self, params)
593
602
594 ret = {
603 ret = {
595 'msg': 'Deleted repository `%s`' % repo_name,
604 'msg': 'Deleted repository `%s`' % repo_name,
596 'success': True
605 'success': True
597 }
606 }
598 expected = ret
607 expected = ret
599 self._compare_ok(id_, expected, given=response.body)
608 self._compare_ok(id_, expected, given=response.body)
600
609
601 def test_api_delete_repo_exception_occurred(self):
610 def test_api_delete_repo_exception_occurred(self):
602 repo_name = 'api_delete_me'
611 repo_name = 'api_delete_me'
603 create_repo(repo_name, self.REPO_TYPE)
612 create_repo(repo_name, self.REPO_TYPE)
604 try:
613 try:
605 with mock.patch.object(RepoModel, 'delete', crash):
614 with mock.patch.object(RepoModel, 'delete', crash):
606 id_, params = _build_data(self.apikey, 'delete_repo',
615 id_, params = _build_data(self.apikey, 'delete_repo',
607 repoid=repo_name,)
616 repoid=repo_name,)
608 response = api_call(self, params)
617 response = api_call(self, params)
609
618
610 expected = 'failed to delete repository `%s`' % repo_name
619 expected = 'failed to delete repository `%s`' % repo_name
611 self._compare_error(id_, expected, given=response.body)
620 self._compare_error(id_, expected, given=response.body)
612 finally:
621 finally:
613 destroy_repo(repo_name)
622 destroy_repo(repo_name)
614
623
615 def test_api_fork_repo(self):
624 def test_api_fork_repo(self):
616 fork_name = 'api-repo-fork'
625 fork_name = 'api-repo-fork'
617 id_, params = _build_data(self.apikey, 'fork_repo',
626 id_, params = _build_data(self.apikey, 'fork_repo',
618 repoid=self.REPO,
627 repoid=self.REPO,
619 fork_name=fork_name,
628 fork_name=fork_name,
620 owner=TEST_USER_ADMIN_LOGIN,
629 owner=TEST_USER_ADMIN_LOGIN,
621 )
630 )
622 response = api_call(self, params)
631 response = api_call(self, params)
623
632
624 ret = {
633 ret = {
625 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
634 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
626 fork_name),
635 fork_name),
627 'success': True
636 'success': True
628 }
637 }
629 expected = ret
638 expected = ret
630 self._compare_ok(id_, expected, given=response.body)
639 self._compare_ok(id_, expected, given=response.body)
631 destroy_repo(fork_name)
640 destroy_repo(fork_name)
632
641
633 def test_api_fork_repo_unknown_owner(self):
642 def test_api_fork_repo_unknown_owner(self):
634 fork_name = 'api-repo-fork'
643 fork_name = 'api-repo-fork'
635 owner = 'i-dont-exist'
644 owner = 'i-dont-exist'
636 id_, params = _build_data(self.apikey, 'fork_repo',
645 id_, params = _build_data(self.apikey, 'fork_repo',
637 repoid=self.REPO,
646 repoid=self.REPO,
638 fork_name=fork_name,
647 fork_name=fork_name,
639 owner=owner,
648 owner=owner,
640 )
649 )
641 response = api_call(self, params)
650 response = api_call(self, params)
642 expected = 'user `%s` does not exist' % owner
651 expected = 'user `%s` does not exist' % owner
643 self._compare_error(id_, expected, given=response.body)
652 self._compare_error(id_, expected, given=response.body)
644
653
645 def test_api_fork_repo_fork_exists(self):
654 def test_api_fork_repo_fork_exists(self):
646 fork_name = 'api-repo-fork'
655 fork_name = 'api-repo-fork'
647 create_fork(fork_name, self.REPO_TYPE, self.REPO)
656 create_fork(fork_name, self.REPO_TYPE, self.REPO)
648
657
649 try:
658 try:
650 fork_name = 'api-repo-fork'
659 fork_name = 'api-repo-fork'
651
660
652 id_, params = _build_data(self.apikey, 'fork_repo',
661 id_, params = _build_data(self.apikey, 'fork_repo',
653 repoid=self.REPO,
662 repoid=self.REPO,
654 fork_name=fork_name,
663 fork_name=fork_name,
655 owner=TEST_USER_ADMIN_LOGIN,
664 owner=TEST_USER_ADMIN_LOGIN,
656 )
665 )
657 response = api_call(self, params)
666 response = api_call(self, params)
658
667
659 expected = "fork `%s` already exist" % fork_name
668 expected = "fork `%s` already exist" % fork_name
660 self._compare_error(id_, expected, given=response.body)
669 self._compare_error(id_, expected, given=response.body)
661 finally:
670 finally:
662 destroy_repo(fork_name)
671 destroy_repo(fork_name)
663
672
664 def test_api_fork_repo_repo_exists(self):
673 def test_api_fork_repo_repo_exists(self):
665 fork_name = self.REPO
674 fork_name = self.REPO
666
675
667 id_, params = _build_data(self.apikey, 'fork_repo',
676 id_, params = _build_data(self.apikey, 'fork_repo',
668 repoid=self.REPO,
677 repoid=self.REPO,
669 fork_name=fork_name,
678 fork_name=fork_name,
670 owner=TEST_USER_ADMIN_LOGIN,
679 owner=TEST_USER_ADMIN_LOGIN,
671 )
680 )
672 response = api_call(self, params)
681 response = api_call(self, params)
673
682
674 expected = "repo `%s` already exist" % fork_name
683 expected = "repo `%s` already exist" % fork_name
675 self._compare_error(id_, expected, given=response.body)
684 self._compare_error(id_, expected, given=response.body)
676
685
677 @mock.patch.object(RepoModel, 'create_fork', crash)
686 @mock.patch.object(RepoModel, 'create_fork', crash)
678 def test_api_fork_repo_exception_occurred(self):
687 def test_api_fork_repo_exception_occurred(self):
679 fork_name = 'api-repo-fork'
688 fork_name = 'api-repo-fork'
680 id_, params = _build_data(self.apikey, 'fork_repo',
689 id_, params = _build_data(self.apikey, 'fork_repo',
681 repoid=self.REPO,
690 repoid=self.REPO,
682 fork_name=fork_name,
691 fork_name=fork_name,
683 owner=TEST_USER_ADMIN_LOGIN,
692 owner=TEST_USER_ADMIN_LOGIN,
684 )
693 )
685 response = api_call(self, params)
694 response = api_call(self, params)
686
695
687 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
696 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
688 fork_name)
697 fork_name)
689 self._compare_error(id_, expected, given=response.body)
698 self._compare_error(id_, expected, given=response.body)
690
699
691 def test_api_get_users_group(self):
700 def test_api_get_users_group(self):
692 id_, params = _build_data(self.apikey, 'get_users_group',
701 id_, params = _build_data(self.apikey, 'get_users_group',
693 usersgroupid=TEST_USERS_GROUP)
702 usersgroupid=TEST_USERS_GROUP)
694 response = api_call(self, params)
703 response = api_call(self, params)
695
704
696 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
705 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
697 members = []
706 members = []
698 for user in users_group.members:
707 for user in users_group.members:
699 user = user.user
708 user = user.user
700 members.append(user.get_api_data())
709 members.append(user.get_api_data())
701
710
702 ret = users_group.get_api_data()
711 ret = users_group.get_api_data()
703 ret['members'] = members
712 ret['members'] = members
704 expected = ret
713 expected = ret
705 self._compare_ok(id_, expected, given=response.body)
714 self._compare_ok(id_, expected, given=response.body)
706
715
707 def test_api_get_users_groups(self):
716 def test_api_get_users_groups(self):
708
717
709 make_users_group('test_users_group2')
718 make_users_group('test_users_group2')
710
719
711 id_, params = _build_data(self.apikey, 'get_users_groups',)
720 id_, params = _build_data(self.apikey, 'get_users_groups',)
712 response = api_call(self, params)
721 response = api_call(self, params)
713
722
714 expected = []
723 expected = []
715 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
724 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
716 users_group = UsersGroupModel().get_group(gr_name)
725 users_group = UsersGroupModel().get_group(gr_name)
717 ret = users_group.get_api_data()
726 ret = users_group.get_api_data()
718 expected.append(ret)
727 expected.append(ret)
719 self._compare_ok(id_, expected, given=response.body)
728 self._compare_ok(id_, expected, given=response.body)
720
729
721 UsersGroupModel().delete(users_group='test_users_group2')
730 UsersGroupModel().delete(users_group='test_users_group2')
722 Session().commit()
731 Session().commit()
723
732
724 def test_api_create_users_group(self):
733 def test_api_create_users_group(self):
725 group_name = 'some_new_group'
734 group_name = 'some_new_group'
726 id_, params = _build_data(self.apikey, 'create_users_group',
735 id_, params = _build_data(self.apikey, 'create_users_group',
727 group_name=group_name)
736 group_name=group_name)
728 response = api_call(self, params)
737 response = api_call(self, params)
729
738
730 ret = {
739 ret = {
731 'msg': 'created new users group `%s`' % group_name,
740 'msg': 'created new users group `%s`' % group_name,
732 'users_group': jsonify(UsersGroupModel()\
741 'users_group': jsonify(UsersGroupModel()\
733 .get_by_name(group_name)\
742 .get_by_name(group_name)\
734 .get_api_data())
743 .get_api_data())
735 }
744 }
736 expected = ret
745 expected = ret
737 self._compare_ok(id_, expected, given=response.body)
746 self._compare_ok(id_, expected, given=response.body)
738
747
739 destroy_users_group(group_name)
748 destroy_users_group(group_name)
740
749
741 def test_api_get_users_group_that_exist(self):
750 def test_api_get_users_group_that_exist(self):
742 id_, params = _build_data(self.apikey, 'create_users_group',
751 id_, params = _build_data(self.apikey, 'create_users_group',
743 group_name=TEST_USERS_GROUP)
752 group_name=TEST_USERS_GROUP)
744 response = api_call(self, params)
753 response = api_call(self, params)
745
754
746 expected = "users group `%s` already exist" % TEST_USERS_GROUP
755 expected = "users group `%s` already exist" % TEST_USERS_GROUP
747 self._compare_error(id_, expected, given=response.body)
756 self._compare_error(id_, expected, given=response.body)
748
757
749 @mock.patch.object(UsersGroupModel, 'create', crash)
758 @mock.patch.object(UsersGroupModel, 'create', crash)
750 def test_api_get_users_group_exception_occurred(self):
759 def test_api_get_users_group_exception_occurred(self):
751 group_name = 'exception_happens'
760 group_name = 'exception_happens'
752 id_, params = _build_data(self.apikey, 'create_users_group',
761 id_, params = _build_data(self.apikey, 'create_users_group',
753 group_name=group_name)
762 group_name=group_name)
754 response = api_call(self, params)
763 response = api_call(self, params)
755
764
756 expected = 'failed to create group `%s`' % group_name
765 expected = 'failed to create group `%s`' % group_name
757 self._compare_error(id_, expected, given=response.body)
766 self._compare_error(id_, expected, given=response.body)
758
767
759 def test_api_add_user_to_users_group(self):
768 def test_api_add_user_to_users_group(self):
760 gr_name = 'test_group'
769 gr_name = 'test_group'
761 UsersGroupModel().create(gr_name)
770 UsersGroupModel().create(gr_name)
762 Session().commit()
771 Session().commit()
763 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
772 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
764 usersgroupid=gr_name,
773 usersgroupid=gr_name,
765 userid=TEST_USER_ADMIN_LOGIN)
774 userid=TEST_USER_ADMIN_LOGIN)
766 response = api_call(self, params)
775 response = api_call(self, params)
767
776
768 expected = {
777 expected = {
769 'msg': 'added member `%s` to users group `%s`' % (
778 'msg': 'added member `%s` to users group `%s`' % (
770 TEST_USER_ADMIN_LOGIN, gr_name
779 TEST_USER_ADMIN_LOGIN, gr_name
771 ),
780 ),
772 'success': True}
781 'success': True}
773 self._compare_ok(id_, expected, given=response.body)
782 self._compare_ok(id_, expected, given=response.body)
774
783
775 UsersGroupModel().delete(users_group=gr_name)
784 UsersGroupModel().delete(users_group=gr_name)
776 Session().commit()
785 Session().commit()
777
786
778 def test_api_add_user_to_users_group_that_doesnt_exist(self):
787 def test_api_add_user_to_users_group_that_doesnt_exist(self):
779 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
788 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
780 usersgroupid='false-group',
789 usersgroupid='false-group',
781 userid=TEST_USER_ADMIN_LOGIN)
790 userid=TEST_USER_ADMIN_LOGIN)
782 response = api_call(self, params)
791 response = api_call(self, params)
783
792
784 expected = 'users group `%s` does not exist' % 'false-group'
793 expected = 'users group `%s` does not exist' % 'false-group'
785 self._compare_error(id_, expected, given=response.body)
794 self._compare_error(id_, expected, given=response.body)
786
795
787 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
796 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
788 def test_api_add_user_to_users_group_exception_occurred(self):
797 def test_api_add_user_to_users_group_exception_occurred(self):
789 gr_name = 'test_group'
798 gr_name = 'test_group'
790 UsersGroupModel().create(gr_name)
799 UsersGroupModel().create(gr_name)
791 Session().commit()
800 Session().commit()
792 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
801 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
793 usersgroupid=gr_name,
802 usersgroupid=gr_name,
794 userid=TEST_USER_ADMIN_LOGIN)
803 userid=TEST_USER_ADMIN_LOGIN)
795 response = api_call(self, params)
804 response = api_call(self, params)
796
805
797 expected = 'failed to add member to users group `%s`' % gr_name
806 expected = 'failed to add member to users group `%s`' % gr_name
798 self._compare_error(id_, expected, given=response.body)
807 self._compare_error(id_, expected, given=response.body)
799
808
800 UsersGroupModel().delete(users_group=gr_name)
809 UsersGroupModel().delete(users_group=gr_name)
801 Session().commit()
810 Session().commit()
802
811
803 def test_api_remove_user_from_users_group(self):
812 def test_api_remove_user_from_users_group(self):
804 gr_name = 'test_group_3'
813 gr_name = 'test_group_3'
805 gr = UsersGroupModel().create(gr_name)
814 gr = UsersGroupModel().create(gr_name)
806 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
815 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
807 Session().commit()
816 Session().commit()
808 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
817 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
809 usersgroupid=gr_name,
818 usersgroupid=gr_name,
810 userid=TEST_USER_ADMIN_LOGIN)
819 userid=TEST_USER_ADMIN_LOGIN)
811 response = api_call(self, params)
820 response = api_call(self, params)
812
821
813 expected = {
822 expected = {
814 'msg': 'removed member `%s` from users group `%s`' % (
823 'msg': 'removed member `%s` from users group `%s`' % (
815 TEST_USER_ADMIN_LOGIN, gr_name
824 TEST_USER_ADMIN_LOGIN, gr_name
816 ),
825 ),
817 'success': True}
826 'success': True}
818 self._compare_ok(id_, expected, given=response.body)
827 self._compare_ok(id_, expected, given=response.body)
819
828
820 UsersGroupModel().delete(users_group=gr_name)
829 UsersGroupModel().delete(users_group=gr_name)
821 Session().commit()
830 Session().commit()
822
831
823 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
832 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
824 def test_api_remove_user_from_users_group_exception_occurred(self):
833 def test_api_remove_user_from_users_group_exception_occurred(self):
825 gr_name = 'test_group_3'
834 gr_name = 'test_group_3'
826 gr = UsersGroupModel().create(gr_name)
835 gr = UsersGroupModel().create(gr_name)
827 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
836 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
828 Session().commit()
837 Session().commit()
829 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
838 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
830 usersgroupid=gr_name,
839 usersgroupid=gr_name,
831 userid=TEST_USER_ADMIN_LOGIN)
840 userid=TEST_USER_ADMIN_LOGIN)
832 response = api_call(self, params)
841 response = api_call(self, params)
833
842
834 expected = 'failed to remove member from users group `%s`' % gr_name
843 expected = 'failed to remove member from users group `%s`' % gr_name
835 self._compare_error(id_, expected, given=response.body)
844 self._compare_error(id_, expected, given=response.body)
836
845
837 UsersGroupModel().delete(users_group=gr_name)
846 UsersGroupModel().delete(users_group=gr_name)
838 Session().commit()
847 Session().commit()
839
848
840 @parameterized.expand([('none', 'repository.none'),
849 @parameterized.expand([('none', 'repository.none'),
841 ('read', 'repository.read'),
850 ('read', 'repository.read'),
842 ('write', 'repository.write'),
851 ('write', 'repository.write'),
843 ('admin', 'repository.admin')])
852 ('admin', 'repository.admin')])
844 def test_api_grant_user_permission(self, name, perm):
853 def test_api_grant_user_permission(self, name, perm):
845 id_, params = _build_data(self.apikey, 'grant_user_permission',
854 id_, params = _build_data(self.apikey, 'grant_user_permission',
846 repoid=self.REPO,
855 repoid=self.REPO,
847 userid=TEST_USER_ADMIN_LOGIN,
856 userid=TEST_USER_ADMIN_LOGIN,
848 perm=perm)
857 perm=perm)
849 response = api_call(self, params)
858 response = api_call(self, params)
850
859
851 ret = {
860 ret = {
852 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
861 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
853 perm, TEST_USER_ADMIN_LOGIN, self.REPO
862 perm, TEST_USER_ADMIN_LOGIN, self.REPO
854 ),
863 ),
855 'success': True
864 'success': True
856 }
865 }
857 expected = ret
866 expected = ret
858 self._compare_ok(id_, expected, given=response.body)
867 self._compare_ok(id_, expected, given=response.body)
859
868
860 def test_api_grant_user_permission_wrong_permission(self):
869 def test_api_grant_user_permission_wrong_permission(self):
861 perm = 'haha.no.permission'
870 perm = 'haha.no.permission'
862 id_, params = _build_data(self.apikey, 'grant_user_permission',
871 id_, params = _build_data(self.apikey, 'grant_user_permission',
863 repoid=self.REPO,
872 repoid=self.REPO,
864 userid=TEST_USER_ADMIN_LOGIN,
873 userid=TEST_USER_ADMIN_LOGIN,
865 perm=perm)
874 perm=perm)
866 response = api_call(self, params)
875 response = api_call(self, params)
867
876
868 expected = 'permission `%s` does not exist' % perm
877 expected = 'permission `%s` does not exist' % perm
869 self._compare_error(id_, expected, given=response.body)
878 self._compare_error(id_, expected, given=response.body)
870
879
871 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
880 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
872 def test_api_grant_user_permission_exception_when_adding(self):
881 def test_api_grant_user_permission_exception_when_adding(self):
873 perm = 'repository.read'
882 perm = 'repository.read'
874 id_, params = _build_data(self.apikey, 'grant_user_permission',
883 id_, params = _build_data(self.apikey, 'grant_user_permission',
875 repoid=self.REPO,
884 repoid=self.REPO,
876 userid=TEST_USER_ADMIN_LOGIN,
885 userid=TEST_USER_ADMIN_LOGIN,
877 perm=perm)
886 perm=perm)
878 response = api_call(self, params)
887 response = api_call(self, params)
879
888
880 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
889 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
881 TEST_USER_ADMIN_LOGIN, self.REPO
890 TEST_USER_ADMIN_LOGIN, self.REPO
882 )
891 )
883 self._compare_error(id_, expected, given=response.body)
892 self._compare_error(id_, expected, given=response.body)
884
893
885 def test_api_revoke_user_permission(self):
894 def test_api_revoke_user_permission(self):
886 id_, params = _build_data(self.apikey, 'revoke_user_permission',
895 id_, params = _build_data(self.apikey, 'revoke_user_permission',
887 repoid=self.REPO,
896 repoid=self.REPO,
888 userid=TEST_USER_ADMIN_LOGIN,)
897 userid=TEST_USER_ADMIN_LOGIN,)
889 response = api_call(self, params)
898 response = api_call(self, params)
890
899
891 expected = {
900 expected = {
892 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
901 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
893 TEST_USER_ADMIN_LOGIN, self.REPO
902 TEST_USER_ADMIN_LOGIN, self.REPO
894 ),
903 ),
895 'success': True
904 'success': True
896 }
905 }
897 self._compare_ok(id_, expected, given=response.body)
906 self._compare_ok(id_, expected, given=response.body)
898
907
899 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
908 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
900 def test_api_revoke_user_permission_exception_when_adding(self):
909 def test_api_revoke_user_permission_exception_when_adding(self):
901 id_, params = _build_data(self.apikey, 'revoke_user_permission',
910 id_, params = _build_data(self.apikey, 'revoke_user_permission',
902 repoid=self.REPO,
911 repoid=self.REPO,
903 userid=TEST_USER_ADMIN_LOGIN,)
912 userid=TEST_USER_ADMIN_LOGIN,)
904 response = api_call(self, params)
913 response = api_call(self, params)
905
914
906 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
915 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
907 TEST_USER_ADMIN_LOGIN, self.REPO
916 TEST_USER_ADMIN_LOGIN, self.REPO
908 )
917 )
909 self._compare_error(id_, expected, given=response.body)
918 self._compare_error(id_, expected, given=response.body)
910
919
911 @parameterized.expand([('none', 'repository.none'),
920 @parameterized.expand([('none', 'repository.none'),
912 ('read', 'repository.read'),
921 ('read', 'repository.read'),
913 ('write', 'repository.write'),
922 ('write', 'repository.write'),
914 ('admin', 'repository.admin')])
923 ('admin', 'repository.admin')])
915 def test_api_grant_users_group_permission(self, name, perm):
924 def test_api_grant_users_group_permission(self, name, perm):
916 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
925 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
917 repoid=self.REPO,
926 repoid=self.REPO,
918 usersgroupid=TEST_USERS_GROUP,
927 usersgroupid=TEST_USERS_GROUP,
919 perm=perm)
928 perm=perm)
920 response = api_call(self, params)
929 response = api_call(self, params)
921
930
922 ret = {
931 ret = {
923 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
932 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
924 perm, TEST_USERS_GROUP, self.REPO
933 perm, TEST_USERS_GROUP, self.REPO
925 ),
934 ),
926 'success': True
935 'success': True
927 }
936 }
928 expected = ret
937 expected = ret
929 self._compare_ok(id_, expected, given=response.body)
938 self._compare_ok(id_, expected, given=response.body)
930
939
931 def test_api_grant_users_group_permission_wrong_permission(self):
940 def test_api_grant_users_group_permission_wrong_permission(self):
932 perm = 'haha.no.permission'
941 perm = 'haha.no.permission'
933 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
942 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
934 repoid=self.REPO,
943 repoid=self.REPO,
935 usersgroupid=TEST_USERS_GROUP,
944 usersgroupid=TEST_USERS_GROUP,
936 perm=perm)
945 perm=perm)
937 response = api_call(self, params)
946 response = api_call(self, params)
938
947
939 expected = 'permission `%s` does not exist' % perm
948 expected = 'permission `%s` does not exist' % perm
940 self._compare_error(id_, expected, given=response.body)
949 self._compare_error(id_, expected, given=response.body)
941
950
942 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
951 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
943 def test_api_grant_users_group_permission_exception_when_adding(self):
952 def test_api_grant_users_group_permission_exception_when_adding(self):
944 perm = 'repository.read'
953 perm = 'repository.read'
945 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
954 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
946 repoid=self.REPO,
955 repoid=self.REPO,
947 usersgroupid=TEST_USERS_GROUP,
956 usersgroupid=TEST_USERS_GROUP,
948 perm=perm)
957 perm=perm)
949 response = api_call(self, params)
958 response = api_call(self, params)
950
959
951 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
960 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
952 TEST_USERS_GROUP, self.REPO
961 TEST_USERS_GROUP, self.REPO
953 )
962 )
954 self._compare_error(id_, expected, given=response.body)
963 self._compare_error(id_, expected, given=response.body)
955
964
956 def test_api_revoke_users_group_permission(self):
965 def test_api_revoke_users_group_permission(self):
957 RepoModel().grant_users_group_permission(repo=self.REPO,
966 RepoModel().grant_users_group_permission(repo=self.REPO,
958 group_name=TEST_USERS_GROUP,
967 group_name=TEST_USERS_GROUP,
959 perm='repository.read')
968 perm='repository.read')
960 Session().commit()
969 Session().commit()
961 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
970 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
962 repoid=self.REPO,
971 repoid=self.REPO,
963 usersgroupid=TEST_USERS_GROUP,)
972 usersgroupid=TEST_USERS_GROUP,)
964 response = api_call(self, params)
973 response = api_call(self, params)
965
974
966 expected = {
975 expected = {
967 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
976 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
968 TEST_USERS_GROUP, self.REPO
977 TEST_USERS_GROUP, self.REPO
969 ),
978 ),
970 'success': True
979 'success': True
971 }
980 }
972 self._compare_ok(id_, expected, given=response.body)
981 self._compare_ok(id_, expected, given=response.body)
973
982
974 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
983 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
975 def test_api_revoke_users_group_permission_exception_when_adding(self):
984 def test_api_revoke_users_group_permission_exception_when_adding(self):
976
985
977 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
986 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
978 repoid=self.REPO,
987 repoid=self.REPO,
979 usersgroupid=TEST_USERS_GROUP,)
988 usersgroupid=TEST_USERS_GROUP,)
980 response = api_call(self, params)
989 response = api_call(self, params)
981
990
982 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
991 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
983 TEST_USERS_GROUP, self.REPO
992 TEST_USERS_GROUP, self.REPO
984 )
993 )
985 self._compare_error(id_, expected, given=response.body)
994 self._compare_error(id_, expected, given=response.body)
General Comments 0
You need to be logged in to leave comments. Login now