# HG changeset patch # User Marcin Kuzminski # Date 2011-12-21 00:05:31 # Node ID 702e29ce1e9ba37a9f42923af831bd90a05b2a54 # Parent 8355c17fb6aa5ee93495af66c6ee80af9288d749 backporting #329 into stable diff --git a/docs/api/api.rst b/docs/api/api.rst --- a/docs/api/api.rst +++ b/docs/api/api.rst @@ -11,7 +11,7 @@ with JSON protocol both ways. An url to /_admin/api -All clients need to send JSON data in such format:: +All clients are required to send JSON-RPC spec JSON data:: { "api_key":"", @@ -32,7 +32,7 @@ Simply provide api_key can be found in your user account page -RhodeCode API will return always a JSON formatted answer:: +RhodeCode API will return always a JSON-RPC response:: { "result": "", @@ -220,8 +220,8 @@ OUTPUT:: } error: null -add_user_to_users_groups ------------------------- +add_user_to_users_group +----------------------- Adds a user to a users group. This command can be executed only using api_key belonging to user with admin rights @@ -299,14 +299,14 @@ OUTPUT:: "active" : "", "admin" :  "", "ldap" : "", - "permission" : "repository_(read|write|admin)" + "permission" : "repository.(read|write|admin)" }, … { "id" : "", "name" : "", "active": "", - "permission" : "repository_(read|write|admin)" + "permission" : "repository.(read|write|admin)" }, … ] @@ -353,10 +353,27 @@ INPUT:: args: { "repo_name" : "", "user_name" : "", - "perm" : "(None|repository_(read|write|admin))", + "perm" : "(None|repository.(read|write|admin))", } OUTPUT:: result: None error: null + +add_users_group_to_repo +----------------------- + +Add a users group to a repository. This command can be executed only using +api_key belonging to user with admin rights. If "perm" is None, group will +be removed from the repository. + +INPUT:: + + api_key : "" + method : "add_users_group_to_repo" + args: { + "repo_name" : "", + "group_name" : "", + "perm" : "(None|repository.(read|write|admin))", + } \ No newline at end of file diff --git a/rhodecode/controllers/api/__init__.py b/rhodecode/controllers/api/__init__.py --- a/rhodecode/controllers/api/__init__.py +++ b/rhodecode/controllers/api/__init__.py @@ -4,7 +4,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~ JSON RPC controller - + :created_on: Aug 20, 2011 :author: marcink :copyright: (C) 2009-2010 Marcin Kuzminski @@ -36,7 +36,7 @@ from rhodecode.lib.compat import izip_lo from paste.response import replace_header from pylons.controllers import WSGIController -from pylons.controllers.util import Response + from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \ HTTPBadRequest, HTTPError @@ -46,33 +46,40 @@ from rhodecode.lib.auth import AuthUser log = logging.getLogger('JSONRPC') + class JSONRPCError(BaseException): def __init__(self, message): self.message = message + super(JSONRPCError, self).__init__() def __str__(self): return str(self.message) def jsonrpc_error(message, code=None): - """Generate a Response object with a JSON-RPC error body""" - return Response(body=json.dumps(dict(result=None, - error=message))) + """ + Generate a Response object with a JSON-RPC error body + """ + from pylons.controllers.util import Response + resp = Response(body=json.dumps(dict(result=None, error=message)), + status=code, + content_type='application/json') + return resp class JSONRPCController(WSGIController): """ A WSGI-speaking JSON-RPC controller class - + See the specification: `. - + Valid controller return values should be json-serializable objects. - + Sub-classes should catch their exceptions and raise JSONRPCError if they want to pass meaningful errors to the client. - + """ def _get_method_args(self): @@ -104,24 +111,27 @@ class JSONRPCController(WSGIController): try: json_body = json.loads(urllib.unquote_plus(raw_body)) except ValueError, e: - #catch JSON errors Here + # catch JSON errors Here return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \ % (e, urllib.unquote_plus(raw_body))) - #check AUTH based on API KEY + # check AUTH based on API KEY try: self._req_api_key = json_body['api_key'] + self._req_id = json_body['id'] self._req_method = json_body['method'] - self._req_params = json_body['args'] + self._request_params = json_body['args'] log.debug('method: %s, params: %s', self._req_method, - self._req_params) + self._request_params) except KeyError, e: return jsonrpc_error(message='Incorrect JSON query missing %s' % e) - #check if we can find this session using api_key + # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) + if u is None: + return jsonrpc_error(message='Invalid API KEY') auth_u = AuthUser(u.user_id, self._req_api_key) except Exception, e: return jsonrpc_error(message='Invalid API KEY') @@ -136,13 +146,14 @@ class JSONRPCController(WSGIController): # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] - defaults = argspec[3] or [] + defaults = map(type, argspec[3] or []) default_empty = types.NotImplementedType - kwarglist = list(izip_longest(reversed(arglist), reversed(defaults), - fillvalue=default_empty)) + # kw arguments required by this method + func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults), + fillvalue=default_empty)) - # this is little trick to inject logged in user for + # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have # rhodecode_user attribute set self.rhodecode_user = auth_u @@ -157,21 +168,23 @@ class JSONRPCController(WSGIController): (self._func.__name__, USER_SESSION_ATTR)) # get our arglist and check if we provided them as args - for arg, default in kwarglist: + for arg, default in func_kwargs.iteritems(): if arg == USER_SESSION_ATTR: - # USER_SESSION_ATTR is something translated from api key and + # USER_SESSION_ATTR is something translated from api key and # this is checked before so we don't need validate it continue - # skip the required param check if it's default value is + # skip the required param check if it's default value is # NotImplementedType (default_empty) - if not self._req_params or (type(default) == default_empty - and arg not in self._req_params): - return jsonrpc_error(message=('Missing non optional %s arg ' - 'in JSON DATA') % arg) + if (default == default_empty and arg not in self._request_params): + return jsonrpc_error( + message=( + 'Missing non optional `%s` arg in JSON DATA' % arg + ) + ) - self._rpc_args = {USER_SESSION_ATTR:u} - self._rpc_args.update(self._req_params) + self._rpc_args = {USER_SESSION_ATTR: u} + self._rpc_args.update(self._request_params) self._rpc_args['action'] = self._req_method self._rpc_args['environ'] = environ @@ -180,6 +193,7 @@ class JSONRPCController(WSGIController): status = [] headers = [] exc_info = [] + def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) @@ -212,7 +226,8 @@ class JSONRPCController(WSGIController): if self._error is not None: raw_response = None - response = dict(result=raw_response, error=self._error) + response = dict(result=raw_response, + error=self._error) try: return json.dumps(response) diff --git a/rhodecode/model/repo_permission.py b/rhodecode/model/repo_permission.py --- a/rhodecode/model/repo_permission.py +++ b/rhodecode/model/repo_permission.py @@ -6,8 +6,9 @@ repository permission model for RhodeCode :created_on: Oct 1, 2011 - :author: nvinot + :author: nvinot, marcink :copyright: (C) 2011-2011 Nicolas Vinot + :copyright: (C) 2009-2011 Marcin Kuzminski :license: GPLv3, see COPYING for more details. """ # This program is free software: you can redistribute it and/or modify @@ -24,11 +25,13 @@ # along with this program. If not, see . import logging -from rhodecode.model.db import BaseModel, RepoToPerm, Permission +from rhodecode.model.db import BaseModel, RepoToPerm, Permission,\ + UsersGroupRepoToPerm from rhodecode.model.meta import Session log = logging.getLogger(__name__) + class RepositoryPermissionModel(BaseModel): def get_user_permission(self, repository, user): return RepoToPerm.query() \ @@ -56,8 +59,43 @@ class RepositoryPermissionModel(BaseMode Session.delete(current) Session.commit() + def get_users_group_permission(self, repository, users_group): + return UsersGroupRepoToPerm.query() \ + .filter(UsersGroupRepoToPerm.users_group == users_group) \ + .filter(UsersGroupRepoToPerm.repository == repository) \ + .scalar() + + def update_users_group_permission(self, repository, users_group, + permission): + permission = Permission.get_by_key(permission) + current = self.get_users_group_permission(repository, users_group) + if current: + if not current.permission is permission: + current.permission = permission + else: + p = UsersGroupRepoToPerm() + p.users_group = users_group + p.repository = repository + p.permission = permission + self.sa.add(p) + Session.commit() + + def delete_users_group_permission(self, repository, users_group): + current = self.get_users_group_permission(repository, users_group) + if current: + self.sa.delete(current) + Session.commit() + def update_or_delete_user_permission(self, repository, user, permission): if permission: self.update_user_permission(repository, user, permission) else: self.delete_user_permission(repository, user) + + def update_or_delete_users_group_permission(self, repository, user_group, + permission): + if permission: + self.update_users_group_permission(repository, user_group, + permission) + else: + self.delete_users_group_permission(repository, user_group)