##// END OF EJS Templates
backporting #329 into stable
marcink -
r1794:702e29ce default
parent child Browse files
Show More
@@ -11,7 +11,7 with JSON protocol both ways. An url to
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>",
@@ -32,7 +32,7 Simply provide
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>",
@@ -220,8 +220,8 OUTPUT::
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
@@ -299,14 +299,14 OUTPUT::
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 ]
@@ -353,10 +353,27 INPUT::
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
@@ -4,7 +4,7
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>
@@ -36,7 +36,7 from rhodecode.lib.compat import izip_lo
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
@@ -46,33 +46,40 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):
@@ -104,24 +111,27 class JSONRPCController(WSGIController):
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')
@@ -136,13 +146,14 class JSONRPCController(WSGIController):
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
@@ -157,21 +168,23 class JSONRPCController(WSGIController):
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
@@ -180,6 +193,7 class JSONRPCController(WSGIController):
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)
@@ -212,7 +226,8 class JSONRPCController(WSGIController):
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)
@@ -6,8 +6,9
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
@@ -24,11 +25,13
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() \
@@ -56,8 +59,43 class RepositoryPermissionModel(BaseMode
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