##// END OF EJS Templates
backporting #329 into stable
marcink -
r1794:702e29ce default
parent child Browse files
Show More
@@ -1,362 +1,379
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 in RhodeCode is
10 with JSON protocol both ways. An url to send API request in RhodeCode is
11 <your_server>/_admin/api
11 <your_server>/_admin/api
12
12
13
13
14 All clients need to send JSON data in such format::
14 All clients are required to send JSON-RPC spec JSON data::
15
15
16 {
16 {
17 "api_key":"<api_key>",
17 "api_key":"<api_key>",
18 "method":"<method_name>",
18 "method":"<method_name>",
19 "args":{"<arg_key>":"<arg_val>"}
19 "args":{"<arg_key>":"<arg_val>"}
20 }
20 }
21
21
22 Example call for autopulling remotes repos using curl::
22 Example call for autopulling remotes repos using curl::
23 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
23 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
24
24
25 Simply provide
25 Simply provide
26 - *api_key* for access and permission validation.
26 - *api_key* for access and permission validation.
27 - *method* is name of method to call
27 - *method* is name of method to call
28 - *args* is an key:value list of arguments to pass to method
28 - *args* is an key:value list of arguments to pass to method
29
29
30 .. note::
30 .. note::
31
31
32 api_key can be found in your user account page
32 api_key can be found in your user account page
33
33
34
34
35 RhodeCode API will return always a JSON formatted answer::
35 RhodeCode API will return always a JSON-RPC response::
36
36
37 {
37 {
38 "result": "<result>",
38 "result": "<result>",
39 "error": null
39 "error": null
40 }
40 }
41
41
42 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
42 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
43 calling api *error* key from response will contain failure description
43 calling api *error* key from response will contain failure description
44 and result will be null.
44 and result will be null.
45
45
46 API METHODS
46 API METHODS
47 +++++++++++
47 +++++++++++
48
48
49
49
50 pull
50 pull
51 ----
51 ----
52
52
53 Pulls given repo from remote location. Can be used to automatically keep
53 Pulls given repo from remote location. Can be used to automatically keep
54 remote repos up to date. This command can be executed only using api_key
54 remote repos up to date. This command can be executed only using api_key
55 belonging to user with admin rights
55 belonging to user with admin rights
56
56
57 INPUT::
57 INPUT::
58
58
59 api_key : "<api_key>"
59 api_key : "<api_key>"
60 method : "pull"
60 method : "pull"
61 args : {
61 args : {
62 "repo" : "<repo_name>"
62 "repo" : "<repo_name>"
63 }
63 }
64
64
65 OUTPUT::
65 OUTPUT::
66
66
67 result : "Pulled from <repo_name>"
67 result : "Pulled from <repo_name>"
68 error : null
68 error : null
69
69
70
70
71 get_users
71 get_users
72 ---------
72 ---------
73
73
74 Lists all existing users. This command can be executed only using api_key
74 Lists all existing users. This command can be executed only using api_key
75 belonging to user with admin rights.
75 belonging to user with admin rights.
76
76
77 INPUT::
77 INPUT::
78
78
79 api_key : "<api_key>"
79 api_key : "<api_key>"
80 method : "get_users"
80 method : "get_users"
81 args : { }
81 args : { }
82
82
83 OUTPUT::
83 OUTPUT::
84
84
85 result: [
85 result: [
86 {
86 {
87 "id" : "<id>",
87 "id" : "<id>",
88 "username" : "<username>",
88 "username" : "<username>",
89 "firstname": "<firstname>",
89 "firstname": "<firstname>",
90 "lastname" : "<lastname>",
90 "lastname" : "<lastname>",
91 "email" : "<email>",
91 "email" : "<email>",
92 "active" : "<bool>",
92 "active" : "<bool>",
93 "admin" :Β  "<bool>",
93 "admin" :Β  "<bool>",
94 "ldap" : "<ldap_dn>"
94 "ldap" : "<ldap_dn>"
95 },
95 },
96 …
96 …
97 ]
97 ]
98 error: null
98 error: null
99
99
100 create_user
100 create_user
101 -----------
101 -----------
102
102
103 Creates new user in RhodeCode. This command can be executed only using api_key
103 Creates new user in RhodeCode. This command can be executed only using api_key
104 belonging to user with admin rights.
104 belonging to user with admin rights.
105
105
106 INPUT::
106 INPUT::
107
107
108 api_key : "<api_key>"
108 api_key : "<api_key>"
109 method : "create_user"
109 method : "create_user"
110 args : {
110 args : {
111 "username" : "<username>",
111 "username" : "<username>",
112 "password" : "<password>",
112 "password" : "<password>",
113 "firstname" : "<firstname>",
113 "firstname" : "<firstname>",
114 "lastname" : "<lastname>",
114 "lastname" : "<lastname>",
115 "email" : "<useremail>"
115 "email" : "<useremail>"
116 "active" : "<bool> = True",
116 "active" : "<bool> = True",
117 "admin" : "<bool> = False",
117 "admin" : "<bool> = False",
118 "ldap_dn" : "<ldap_dn> = None"
118 "ldap_dn" : "<ldap_dn> = None"
119 }
119 }
120
120
121 OUTPUT::
121 OUTPUT::
122
122
123 result: {
123 result: {
124 "msg" : "created new user <username>"
124 "msg" : "created new user <username>"
125 }
125 }
126 error: null
126 error: null
127
127
128 get_users_groups
128 get_users_groups
129 ----------------
129 ----------------
130
130
131 Lists all existing users groups. This command can be executed only using api_key
131 Lists all existing users groups. This command can be executed only using api_key
132 belonging to user with admin rights.
132 belonging to user with admin rights.
133
133
134 INPUT::
134 INPUT::
135
135
136 api_key : "<api_key>"
136 api_key : "<api_key>"
137 method : "get_users_groups"
137 method : "get_users_groups"
138 args : { }
138 args : { }
139
139
140 OUTPUT::
140 OUTPUT::
141
141
142 result : [
142 result : [
143 {
143 {
144 "id" : "<id>",
144 "id" : "<id>",
145 "name" : "<name>",
145 "name" : "<name>",
146 "active": "<bool>",
146 "active": "<bool>",
147 "members" : [
147 "members" : [
148 {
148 {
149 "id" : "<userid>",
149 "id" : "<userid>",
150 "username" : "<username>",
150 "username" : "<username>",
151 "firstname": "<firstname>",
151 "firstname": "<firstname>",
152 "lastname" : "<lastname>",
152 "lastname" : "<lastname>",
153 "email" : "<email>",
153 "email" : "<email>",
154 "active" : "<bool>",
154 "active" : "<bool>",
155 "admin" :Β  "<bool>",
155 "admin" :Β  "<bool>",
156 "ldap" : "<ldap_dn>"
156 "ldap" : "<ldap_dn>"
157 },
157 },
158 …
158 …
159 ]
159 ]
160 }
160 }
161 ]
161 ]
162 error : null
162 error : null
163
163
164 get_users_group
164 get_users_group
165 ---------------
165 ---------------
166
166
167 Gets an existing users group. This command can be executed only using api_key
167 Gets an existing users group. This command can be executed only using api_key
168 belonging to user with admin rights.
168 belonging to user with admin rights.
169
169
170 INPUT::
170 INPUT::
171
171
172 api_key : "<api_key>"
172 api_key : "<api_key>"
173 method : "get_users_group"
173 method : "get_users_group"
174 args : {
174 args : {
175 "group_name" : "<name>"
175 "group_name" : "<name>"
176 }
176 }
177
177
178 OUTPUT::
178 OUTPUT::
179
179
180 result : None if group not exist
180 result : None if group not exist
181 {
181 {
182 "id" : "<id>",
182 "id" : "<id>",
183 "name" : "<name>",
183 "name" : "<name>",
184 "active": "<bool>",
184 "active": "<bool>",
185 "members" : [
185 "members" : [
186 { "id" : "<userid>",
186 { "id" : "<userid>",
187 "username" : "<username>",
187 "username" : "<username>",
188 "firstname": "<firstname>",
188 "firstname": "<firstname>",
189 "lastname" : "<lastname>",
189 "lastname" : "<lastname>",
190 "email" : "<email>",
190 "email" : "<email>",
191 "active" : "<bool>",
191 "active" : "<bool>",
192 "admin" :Β  "<bool>",
192 "admin" :Β  "<bool>",
193 "ldap" : "<ldap_dn>"
193 "ldap" : "<ldap_dn>"
194 },
194 },
195 …
195 …
196 ]
196 ]
197 }
197 }
198 error : null
198 error : null
199
199
200 create_users_group
200 create_users_group
201 ------------------
201 ------------------
202
202
203 Creates new users group. This command can be executed only using api_key
203 Creates new users group. This command can be executed only using api_key
204 belonging to user with admin rights
204 belonging to user with admin rights
205
205
206 INPUT::
206 INPUT::
207
207
208 api_key : "<api_key>"
208 api_key : "<api_key>"
209 method : "create_users_group"
209 method : "create_users_group"
210 args: {
210 args: {
211 "name": "<name>",
211 "name": "<name>",
212 "active":"<bool> = True"
212 "active":"<bool> = True"
213 }
213 }
214
214
215 OUTPUT::
215 OUTPUT::
216
216
217 result: {
217 result: {
218 "id": "<newusersgroupid>",
218 "id": "<newusersgroupid>",
219 "msg": "created new users group <name>"
219 "msg": "created new users group <name>"
220 }
220 }
221 error: null
221 error: null
222
222
223 add_user_to_users_groups
223 add_user_to_users_group
224 ------------------------
224 -----------------------
225
225
226 Adds a user to a users group. This command can be executed only using api_key
226 Adds a user to a users group. This command can be executed only using api_key
227 belonging to user with admin rights
227 belonging to user with admin rights
228
228
229 INPUT::
229 INPUT::
230
230
231 api_key : "<api_key>"
231 api_key : "<api_key>"
232 method : "add_user_users_group"
232 method : "add_user_users_group"
233 args: {
233 args: {
234 "group_name" : "<groupname>",
234 "group_name" : "<groupname>",
235 "user_name" : "<username>"
235 "user_name" : "<username>"
236 }
236 }
237
237
238 OUTPUT::
238 OUTPUT::
239
239
240 result: {
240 result: {
241 "id": "<newusersgroupmemberid>",
241 "id": "<newusersgroupmemberid>",
242 "msg": "created new users group member"
242 "msg": "created new users group member"
243 }
243 }
244 error: null
244 error: null
245
245
246 get_repos
246 get_repos
247 ---------
247 ---------
248
248
249 Lists all existing repositories. This command can be executed only using api_key
249 Lists all existing repositories. This command can be executed only using api_key
250 belonging to user with admin rights
250 belonging to user with admin rights
251
251
252 INPUT::
252 INPUT::
253
253
254 api_key : "<api_key>"
254 api_key : "<api_key>"
255 method : "get_repos"
255 method : "get_repos"
256 args: { }
256 args: { }
257
257
258 OUTPUT::
258 OUTPUT::
259
259
260 result: [
260 result: [
261 {
261 {
262 "id" : "<id>",
262 "id" : "<id>",
263 "name" : "<name>"
263 "name" : "<name>"
264 "type" : "<type>",
264 "type" : "<type>",
265 "description" : "<description>"
265 "description" : "<description>"
266 },
266 },
267 …
267 …
268 ]
268 ]
269 error: null
269 error: null
270
270
271 get_repo
271 get_repo
272 --------
272 --------
273
273
274 Gets an existing repository. This command can be executed only using api_key
274 Gets an existing repository. This command can be executed only using api_key
275 belonging to user with admin rights
275 belonging to user with admin rights
276
276
277 INPUT::
277 INPUT::
278
278
279 api_key : "<api_key>"
279 api_key : "<api_key>"
280 method : "get_repo"
280 method : "get_repo"
281 args: {
281 args: {
282 "name" : "<name>"
282 "name" : "<name>"
283 }
283 }
284
284
285 OUTPUT::
285 OUTPUT::
286
286
287 result: None if repository not exist
287 result: None if repository not exist
288 {
288 {
289 "id" : "<id>",
289 "id" : "<id>",
290 "name" : "<name>"
290 "name" : "<name>"
291 "type" : "<type>",
291 "type" : "<type>",
292 "description" : "<description>",
292 "description" : "<description>",
293 "members" : [
293 "members" : [
294 { "id" : "<userid>",
294 { "id" : "<userid>",
295 "username" : "<username>",
295 "username" : "<username>",
296 "firstname": "<firstname>",
296 "firstname": "<firstname>",
297 "lastname" : "<lastname>",
297 "lastname" : "<lastname>",
298 "email" : "<email>",
298 "email" : "<email>",
299 "active" : "<bool>",
299 "active" : "<bool>",
300 "admin" :Β  "<bool>",
300 "admin" :Β  "<bool>",
301 "ldap" : "<ldap_dn>",
301 "ldap" : "<ldap_dn>",
302 "permission" : "repository_(read|write|admin)"
302 "permission" : "repository.(read|write|admin)"
303 },
303 },
304 …
304 …
305 {
305 {
306 "id" : "<usersgroupid>",
306 "id" : "<usersgroupid>",
307 "name" : "<usersgroupname>",
307 "name" : "<usersgroupname>",
308 "active": "<bool>",
308 "active": "<bool>",
309 "permission" : "repository_(read|write|admin)"
309 "permission" : "repository.(read|write|admin)"
310 },
310 },
311 …
311 …
312 ]
312 ]
313 }
313 }
314 error: null
314 error: null
315
315
316 create_repo
316 create_repo
317 -----------
317 -----------
318
318
319 Creates a repository. This command can be executed only using api_key
319 Creates a repository. This command can be executed only using api_key
320 belonging to user with admin rights.
320 belonging to user with admin rights.
321 If repository name contains "/", all needed repository groups will be created.
321 If repository name contains "/", all needed repository groups will be created.
322 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
322 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
323 and create "baz" repository with "bar" as group.
323 and create "baz" repository with "bar" as group.
324
324
325 INPUT::
325 INPUT::
326
326
327 api_key : "<api_key>"
327 api_key : "<api_key>"
328 method : "create_repo"
328 method : "create_repo"
329 args: {
329 args: {
330 "name" : "<name>",
330 "name" : "<name>",
331 "owner_name" : "<ownername>",
331 "owner_name" : "<ownername>",
332 "description" : "<description> = ''",
332 "description" : "<description> = ''",
333 "repo_type" : "<type> = 'hg'",
333 "repo_type" : "<type> = 'hg'",
334 "private" : "<bool> = False"
334 "private" : "<bool> = False"
335 }
335 }
336
336
337 OUTPUT::
337 OUTPUT::
338
338
339 result: None
339 result: None
340 error: null
340 error: null
341
341
342 add_user_to_repo
342 add_user_to_repo
343 ----------------
343 ----------------
344
344
345 Add a user to a repository. This command can be executed only using api_key
345 Add a user to a repository. This command can be executed only using api_key
346 belonging to user with admin rights.
346 belonging to user with admin rights.
347 If "perm" is None, user will be removed from the repository.
347 If "perm" is None, user will be removed from the repository.
348
348
349 INPUT::
349 INPUT::
350
350
351 api_key : "<api_key>"
351 api_key : "<api_key>"
352 method : "add_user_to_repo"
352 method : "add_user_to_repo"
353 args: {
353 args: {
354 "repo_name" : "<reponame>",
354 "repo_name" : "<reponame>",
355 "user_name" : "<username>",
355 "user_name" : "<username>",
356 "perm" : "(None|repository_(read|write|admin))",
356 "perm" : "(None|repository.(read|write|admin))",
357 }
357 }
358
358
359 OUTPUT::
359 OUTPUT::
360
360
361 result: None
361 result: None
362 error: null
362 error: null
363
364 add_users_group_to_repo
365 -----------------------
366
367 Add a users group to a repository. This command can be executed only using
368 api_key belonging to user with admin rights. If "perm" is None, group will
369 be removed from the repository.
370
371 INPUT::
372
373 api_key : "<api_key>"
374 method : "add_users_group_to_repo"
375 args: {
376 "repo_name" : "<reponame>",
377 "group_name" : "<groupname>",
378 "perm" : "(None|repository.(read|write|admin))",
379 } No newline at end of file
@@ -1,242 +1,257
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 JSON RPC controller
6 JSON RPC controller
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 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 inspect
28 import inspect
29 import logging
29 import logging
30 import types
30 import types
31 import urllib
31 import urllib
32 import traceback
32 import traceback
33
33
34 from rhodecode.lib.compat import izip_longest, json
34 from rhodecode.lib.compat import izip_longest, json
35
35
36 from paste.response import replace_header
36 from paste.response import replace_header
37
37
38 from pylons.controllers import WSGIController
38 from pylons.controllers import WSGIController
39 from pylons.controllers.util import Response
39
40
40
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
42 HTTPBadRequest, HTTPError
42 HTTPBadRequest, HTTPError
43
43
44 from rhodecode.model.db import User
44 from rhodecode.model.db import User
45 from rhodecode.lib.auth import AuthUser
45 from rhodecode.lib.auth import AuthUser
46
46
47 log = logging.getLogger('JSONRPC')
47 log = logging.getLogger('JSONRPC')
48
48
49
49 class JSONRPCError(BaseException):
50 class JSONRPCError(BaseException):
50
51
51 def __init__(self, message):
52 def __init__(self, message):
52 self.message = message
53 self.message = message
54 super(JSONRPCError, self).__init__()
53
55
54 def __str__(self):
56 def __str__(self):
55 return str(self.message)
57 return str(self.message)
56
58
57
59
58 def jsonrpc_error(message, code=None):
60 def jsonrpc_error(message, code=None):
59 """Generate a Response object with a JSON-RPC error body"""
61 """
60 return Response(body=json.dumps(dict(result=None,
62 Generate a Response object with a JSON-RPC error body
61 error=message)))
63 """
64 from pylons.controllers.util import Response
65 resp = Response(body=json.dumps(dict(result=None, error=message)),
66 status=code,
67 content_type='application/json')
68 return resp
62
69
63
70
64 class JSONRPCController(WSGIController):
71 class JSONRPCController(WSGIController):
65 """
72 """
66 A WSGI-speaking JSON-RPC controller class
73 A WSGI-speaking JSON-RPC controller class
67
74
68 See the specification:
75 See the specification:
69 <http://json-rpc.org/wiki/specification>`.
76 <http://json-rpc.org/wiki/specification>`.
70
77
71 Valid controller return values should be json-serializable objects.
78 Valid controller return values should be json-serializable objects.
72
79
73 Sub-classes should catch their exceptions and raise JSONRPCError
80 Sub-classes should catch their exceptions and raise JSONRPCError
74 if they want to pass meaningful errors to the client.
81 if they want to pass meaningful errors to the client.
75
82
76 """
83 """
77
84
78 def _get_method_args(self):
85 def _get_method_args(self):
79 """
86 """
80 Return `self._rpc_args` to dispatched controller method
87 Return `self._rpc_args` to dispatched controller method
81 chosen by __call__
88 chosen by __call__
82 """
89 """
83 return self._rpc_args
90 return self._rpc_args
84
91
85 def __call__(self, environ, start_response):
92 def __call__(self, environ, start_response):
86 """
93 """
87 Parse the request body as JSON, look up the method on the
94 Parse the request body as JSON, look up the method on the
88 controller and if it exists, dispatch to it.
95 controller and if it exists, dispatch to it.
89 """
96 """
90 if 'CONTENT_LENGTH' not in environ:
97 if 'CONTENT_LENGTH' not in environ:
91 log.debug("No Content-Length")
98 log.debug("No Content-Length")
92 return jsonrpc_error(message="No Content-Length in request")
99 return jsonrpc_error(message="No Content-Length in request")
93 else:
100 else:
94 length = environ['CONTENT_LENGTH'] or 0
101 length = environ['CONTENT_LENGTH'] or 0
95 length = int(environ['CONTENT_LENGTH'])
102 length = int(environ['CONTENT_LENGTH'])
96 log.debug('Content-Length: %s', length)
103 log.debug('Content-Length: %s', length)
97
104
98 if length == 0:
105 if length == 0:
99 log.debug("Content-Length is 0")
106 log.debug("Content-Length is 0")
100 return jsonrpc_error(message="Content-Length is 0")
107 return jsonrpc_error(message="Content-Length is 0")
101
108
102 raw_body = environ['wsgi.input'].read(length)
109 raw_body = environ['wsgi.input'].read(length)
103
110
104 try:
111 try:
105 json_body = json.loads(urllib.unquote_plus(raw_body))
112 json_body = json.loads(urllib.unquote_plus(raw_body))
106 except ValueError, e:
113 except ValueError, e:
107 #catch JSON errors Here
114 # catch JSON errors Here
108 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
115 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
109 % (e, urllib.unquote_plus(raw_body)))
116 % (e, urllib.unquote_plus(raw_body)))
110
117
111 #check AUTH based on API KEY
118 # check AUTH based on API KEY
112 try:
119 try:
113 self._req_api_key = json_body['api_key']
120 self._req_api_key = json_body['api_key']
121 self._req_id = json_body['id']
114 self._req_method = json_body['method']
122 self._req_method = json_body['method']
115 self._req_params = json_body['args']
123 self._request_params = json_body['args']
116 log.debug('method: %s, params: %s',
124 log.debug('method: %s, params: %s',
117 self._req_method,
125 self._req_method,
118 self._req_params)
126 self._request_params)
119 except KeyError, e:
127 except KeyError, e:
120 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
128 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
121
129
122 #check if we can find this session using api_key
130 # check if we can find this session using api_key
123 try:
131 try:
124 u = User.get_by_api_key(self._req_api_key)
132 u = User.get_by_api_key(self._req_api_key)
133 if u is None:
134 return jsonrpc_error(message='Invalid API KEY')
125 auth_u = AuthUser(u.user_id, self._req_api_key)
135 auth_u = AuthUser(u.user_id, self._req_api_key)
126 except Exception, e:
136 except Exception, e:
127 return jsonrpc_error(message='Invalid API KEY')
137 return jsonrpc_error(message='Invalid API KEY')
128
138
129 self._error = None
139 self._error = None
130 try:
140 try:
131 self._func = self._find_method()
141 self._func = self._find_method()
132 except AttributeError, e:
142 except AttributeError, e:
133 return jsonrpc_error(message=str(e))
143 return jsonrpc_error(message=str(e))
134
144
135 # now that we have a method, add self._req_params to
145 # now that we have a method, add self._req_params to
136 # self.kargs and dispatch control to WGIController
146 # self.kargs and dispatch control to WGIController
137 argspec = inspect.getargspec(self._func)
147 argspec = inspect.getargspec(self._func)
138 arglist = argspec[0][1:]
148 arglist = argspec[0][1:]
139 defaults = argspec[3] or []
149 defaults = map(type, argspec[3] or [])
140 default_empty = types.NotImplementedType
150 default_empty = types.NotImplementedType
141
151
142 kwarglist = list(izip_longest(reversed(arglist), reversed(defaults),
152 # kw arguments required by this method
143 fillvalue=default_empty))
153 func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults),
154 fillvalue=default_empty))
144
155
145 # this is little trick to inject logged in user for
156 # this is little trick to inject logged in user for
146 # perms decorators to work they expect the controller class to have
157 # perms decorators to work they expect the controller class to have
147 # rhodecode_user attribute set
158 # rhodecode_user attribute set
148 self.rhodecode_user = auth_u
159 self.rhodecode_user = auth_u
149
160
150 # This attribute will need to be first param of a method that uses
161 # This attribute will need to be first param of a method that uses
151 # api_key, which is translated to instance of user at that name
162 # api_key, which is translated to instance of user at that name
152 USER_SESSION_ATTR = 'apiuser'
163 USER_SESSION_ATTR = 'apiuser'
153
164
154 if USER_SESSION_ATTR not in arglist:
165 if USER_SESSION_ATTR not in arglist:
155 return jsonrpc_error(message='This method [%s] does not support '
166 return jsonrpc_error(message='This method [%s] does not support '
156 'authentication (missing %s param)' %
167 'authentication (missing %s param)' %
157 (self._func.__name__, USER_SESSION_ATTR))
168 (self._func.__name__, USER_SESSION_ATTR))
158
169
159 # get our arglist and check if we provided them as args
170 # get our arglist and check if we provided them as args
160 for arg, default in kwarglist:
171 for arg, default in func_kwargs.iteritems():
161 if arg == USER_SESSION_ATTR:
172 if arg == USER_SESSION_ATTR:
162 # USER_SESSION_ATTR is something translated from api key and
173 # USER_SESSION_ATTR is something translated from api key and
163 # this is checked before so we don't need validate it
174 # this is checked before so we don't need validate it
164 continue
175 continue
165
176
166 # skip the required param check if it's default value is
177 # skip the required param check if it's default value is
167 # NotImplementedType (default_empty)
178 # NotImplementedType (default_empty)
168 if not self._req_params or (type(default) == default_empty
179 if (default == default_empty and arg not in self._request_params):
169 and arg not in self._req_params):
180 return jsonrpc_error(
170 return jsonrpc_error(message=('Missing non optional %s arg '
181 message=(
171 'in JSON DATA') % arg)
182 'Missing non optional `%s` arg in JSON DATA' % arg
183 )
184 )
172
185
173 self._rpc_args = {USER_SESSION_ATTR:u}
186 self._rpc_args = {USER_SESSION_ATTR: u}
174 self._rpc_args.update(self._req_params)
187 self._rpc_args.update(self._request_params)
175
188
176 self._rpc_args['action'] = self._req_method
189 self._rpc_args['action'] = self._req_method
177 self._rpc_args['environ'] = environ
190 self._rpc_args['environ'] = environ
178 self._rpc_args['start_response'] = start_response
191 self._rpc_args['start_response'] = start_response
179
192
180 status = []
193 status = []
181 headers = []
194 headers = []
182 exc_info = []
195 exc_info = []
196
183 def change_content(new_status, new_headers, new_exc_info=None):
197 def change_content(new_status, new_headers, new_exc_info=None):
184 status.append(new_status)
198 status.append(new_status)
185 headers.extend(new_headers)
199 headers.extend(new_headers)
186 exc_info.append(new_exc_info)
200 exc_info.append(new_exc_info)
187
201
188 output = WSGIController.__call__(self, environ, change_content)
202 output = WSGIController.__call__(self, environ, change_content)
189 output = list(output)
203 output = list(output)
190 headers.append(('Content-Length', str(len(output[0]))))
204 headers.append(('Content-Length', str(len(output[0]))))
191 replace_header(headers, 'Content-Type', 'application/json')
205 replace_header(headers, 'Content-Type', 'application/json')
192 start_response(status[0], headers, exc_info[0])
206 start_response(status[0], headers, exc_info[0])
193
207
194 return output
208 return output
195
209
196 def _dispatch_call(self):
210 def _dispatch_call(self):
197 """
211 """
198 Implement dispatch interface specified by WSGIController
212 Implement dispatch interface specified by WSGIController
199 """
213 """
200 try:
214 try:
201 raw_response = self._inspect_call(self._func)
215 raw_response = self._inspect_call(self._func)
202 if isinstance(raw_response, HTTPError):
216 if isinstance(raw_response, HTTPError):
203 self._error = str(raw_response)
217 self._error = str(raw_response)
204 except JSONRPCError, e:
218 except JSONRPCError, e:
205 self._error = str(e)
219 self._error = str(e)
206 except Exception, e:
220 except Exception, e:
207 log.error('Encountered unhandled exception: %s' \
221 log.error('Encountered unhandled exception: %s' \
208 % traceback.format_exc())
222 % traceback.format_exc())
209 json_exc = JSONRPCError('Internal server error')
223 json_exc = JSONRPCError('Internal server error')
210 self._error = str(json_exc)
224 self._error = str(json_exc)
211
225
212 if self._error is not None:
226 if self._error is not None:
213 raw_response = None
227 raw_response = None
214
228
215 response = dict(result=raw_response, error=self._error)
229 response = dict(result=raw_response,
230 error=self._error)
216
231
217 try:
232 try:
218 return json.dumps(response)
233 return json.dumps(response)
219 except TypeError, e:
234 except TypeError, e:
220 log.debug('Error encoding response: %s', e)
235 log.debug('Error encoding response: %s', e)
221 return json.dumps(dict(result=None,
236 return json.dumps(dict(result=None,
222 error="Error encoding response"))
237 error="Error encoding response"))
223
238
224 def _find_method(self):
239 def _find_method(self):
225 """
240 """
226 Return method named by `self._req_method` in controller if able
241 Return method named by `self._req_method` in controller if able
227 """
242 """
228 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
243 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
229 if self._req_method.startswith('_'):
244 if self._req_method.startswith('_'):
230 raise AttributeError("Method not allowed")
245 raise AttributeError("Method not allowed")
231
246
232 try:
247 try:
233 func = getattr(self, self._req_method, None)
248 func = getattr(self, self._req_method, None)
234 except UnicodeEncodeError:
249 except UnicodeEncodeError:
235 raise AttributeError("Problem decoding unicode in requested "
250 raise AttributeError("Problem decoding unicode in requested "
236 "method name.")
251 "method name.")
237
252
238 if isinstance(func, types.MethodType):
253 if isinstance(func, types.MethodType):
239 return func
254 return func
240 else:
255 else:
241 raise AttributeError("No such method: %s" % self._req_method)
256 raise AttributeError("No such method: %s" % self._req_method)
242
257
@@ -1,63 +1,101
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.users_group
3 rhodecode.model.users_group
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 repository permission model for RhodeCode
6 repository permission model for RhodeCode
7
7
8 :created_on: Oct 1, 2011
8 :created_on: Oct 1, 2011
9 :author: nvinot
9 :author: nvinot, marcink
10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
12 """
13 """
13 # This program is free software: you can redistribute it and/or modify
14 # 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
15 # 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
16 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 # (at your option) any later version.
17 #
18 #
18 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 # GNU General Public License for more details.
22 #
23 #
23 # You should have received a copy of the GNU General Public License
24 # 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/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26
26 import logging
27 import logging
27 from rhodecode.model.db import BaseModel, RepoToPerm, Permission
28 from rhodecode.model.db import BaseModel, RepoToPerm, Permission,\
29 UsersGroupRepoToPerm
28 from rhodecode.model.meta import Session
30 from rhodecode.model.meta import Session
29
31
30 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
31
33
34
32 class RepositoryPermissionModel(BaseModel):
35 class RepositoryPermissionModel(BaseModel):
33 def get_user_permission(self, repository, user):
36 def get_user_permission(self, repository, user):
34 return RepoToPerm.query() \
37 return RepoToPerm.query() \
35 .filter(RepoToPerm.user == user) \
38 .filter(RepoToPerm.user == user) \
36 .filter(RepoToPerm.repository == repository) \
39 .filter(RepoToPerm.repository == repository) \
37 .scalar()
40 .scalar()
38
41
39 def update_user_permission(self, repository, user, permission):
42 def update_user_permission(self, repository, user, permission):
40 permission = Permission.get_by_key(permission)
43 permission = Permission.get_by_key(permission)
41 current = self.get_user_permission(repository, user)
44 current = self.get_user_permission(repository, user)
42 if current:
45 if current:
43 if not current.permission is permission:
46 if not current.permission is permission:
44 current.permission = permission
47 current.permission = permission
45 else:
48 else:
46 p = RepoToPerm()
49 p = RepoToPerm()
47 p.user = user
50 p.user = user
48 p.repository = repository
51 p.repository = repository
49 p.permission = permission
52 p.permission = permission
50 Session.add(p)
53 Session.add(p)
51 Session.commit()
54 Session.commit()
52
55
53 def delete_user_permission(self, repository, user):
56 def delete_user_permission(self, repository, user):
54 current = self.get_user_permission(repository, user)
57 current = self.get_user_permission(repository, user)
55 if current:
58 if current:
56 Session.delete(current)
59 Session.delete(current)
57 Session.commit()
60 Session.commit()
58
61
62 def get_users_group_permission(self, repository, users_group):
63 return UsersGroupRepoToPerm.query() \
64 .filter(UsersGroupRepoToPerm.users_group == users_group) \
65 .filter(UsersGroupRepoToPerm.repository == repository) \
66 .scalar()
67
68 def update_users_group_permission(self, repository, users_group,
69 permission):
70 permission = Permission.get_by_key(permission)
71 current = self.get_users_group_permission(repository, users_group)
72 if current:
73 if not current.permission is permission:
74 current.permission = permission
75 else:
76 p = UsersGroupRepoToPerm()
77 p.users_group = users_group
78 p.repository = repository
79 p.permission = permission
80 self.sa.add(p)
81 Session.commit()
82
83 def delete_users_group_permission(self, repository, users_group):
84 current = self.get_users_group_permission(repository, users_group)
85 if current:
86 self.sa.delete(current)
87 Session.commit()
88
59 def update_or_delete_user_permission(self, repository, user, permission):
89 def update_or_delete_user_permission(self, repository, user, permission):
60 if permission:
90 if permission:
61 self.update_user_permission(repository, user, permission)
91 self.update_user_permission(repository, user, permission)
62 else:
92 else:
63 self.delete_user_permission(repository, user)
93 self.delete_user_permission(repository, user)
94
95 def update_or_delete_users_group_permission(self, repository, user_group,
96 permission):
97 if permission:
98 self.update_users_group_permission(repository, user_group,
99 permission)
100 else:
101 self.delete_users_group_permission(repository, user_group)
General Comments 0
You need to be logged in to leave comments. Login now