aboutsummaryrefslogtreecommitdiff
path: root/rhodecode/controllers/api
diff options
context:
space:
mode:
Diffstat (limited to 'rhodecode/controllers/api')
-rw-r--r--rhodecode/controllers/api/__init__.py24
-rw-r--r--rhodecode/controllers/api/api.py170
2 files changed, 162 insertions, 32 deletions
diff --git a/rhodecode/controllers/api/__init__.py b/rhodecode/controllers/api/__init__.py
index 13fd7033..2f4e1416 100644
--- a/rhodecode/controllers/api/__init__.py
+++ b/rhodecode/controllers/api/__init__.py
@@ -32,17 +32,15 @@ import urllib
import traceback
import time
-from rhodecode.lib.compat import izip_longest, json
-
from paste.response import replace_header
-
from pylons.controllers import WSGIController
-
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
HTTPBadRequest, HTTPError
from rhodecode.model.db import User
+from rhodecode.model import meta
+from rhodecode.lib.compat import izip_longest, json
from rhodecode.lib.auth import AuthUser
from rhodecode.lib.base import _get_ip_addr, _get_access_path
from rhodecode.lib.utils2 import safe_unicode
@@ -86,6 +84,9 @@ class JSONRPCController(WSGIController):
"""
+ def _get_ip_addr(self, environ):
+ return _get_ip_addr(environ)
+
def _get_method_args(self):
"""
Return `self._rpc_args` to dispatched controller method
@@ -99,6 +100,7 @@ class JSONRPCController(WSGIController):
controller and if it exists, dispatch to it.
"""
start = time.time()
+ ip_addr = self.ip_addr = self._get_ip_addr(environ)
self._req_id = None
if 'CONTENT_LENGTH' not in environ:
log.debug("No Content-Length")
@@ -130,6 +132,9 @@ class JSONRPCController(WSGIController):
self._req_id = json_body['id']
self._req_method = json_body['method']
self._request_params = json_body['args']
+ if not isinstance(self._request_params, dict):
+ self._request_params = {}
+
log.debug(
'method: %s, params: %s' % (self._req_method,
self._request_params)
@@ -144,7 +149,15 @@ class JSONRPCController(WSGIController):
if u is None:
return jsonrpc_error(retid=self._req_id,
message='Invalid API KEY')
- auth_u = AuthUser(u.user_id, self._req_api_key)
+
+ #check if we are allowed to use this IP
+ auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr)
+ if not auth_u.ip_allowed:
+ return jsonrpc_error(retid=self._req_id,
+ message='request from IP:%s not allowed' % (ip_addr))
+ else:
+ log.info('Access for IP:%s allowed' % (ip_addr))
+
except Exception, e:
return jsonrpc_error(retid=self._req_id,
message='Invalid API KEY')
@@ -202,6 +215,7 @@ class JSONRPCController(WSGIController):
)
self._rpc_args = {USER_SESSION_ATTR: u}
+
self._rpc_args.update(self._request_params)
self._rpc_args['action'] = self._req_method
diff --git a/rhodecode/controllers/api/api.py b/rhodecode/controllers/api/api.py
index 93bec581..fc185df0 100644
--- a/rhodecode/controllers/api/api.py
+++ b/rhodecode/controllers/api/api.py
@@ -27,10 +27,12 @@
import traceback
import logging
+from pylons.controllers.util import abort
from rhodecode.controllers.api import JSONRPCController, JSONRPCError
-from rhodecode.lib.auth import HasPermissionAllDecorator, \
- HasPermissionAnyDecorator, PasswordGenerator, AuthUser
+from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
+ HasPermissionAllDecorator, HasPermissionAnyDecorator, \
+ HasPermissionAnyApi, HasRepoPermissionAnyApi
from rhodecode.lib.utils import map_groups, repo2db_mapper
from rhodecode.model.meta import Session
from rhodecode.model.scm import ScmModel
@@ -38,11 +40,27 @@ from rhodecode.model.repo import RepoModel
from rhodecode.model.user import UserModel
from rhodecode.model.users_group import UsersGroupModel
from rhodecode.model.permission import PermissionModel
-from rhodecode.model.db import Repository
+from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
log = logging.getLogger(__name__)
+class OptionalAttr(object):
+ """
+ Special Optional Option that defines other attribute
+ """
+ def __init__(self, attr_name):
+ self.attr_name = attr_name
+
+ def __repr__(self):
+ return '<OptionalAttr:%s>' % self.attr_name
+
+ def __call__(self):
+ return self
+#alias
+OAttr = OptionalAttr
+
+
class Optional(object):
"""
Defines an optional parameter::
@@ -184,10 +202,11 @@ class ApiController(JSONRPCController):
'Error occurred during rescan repositories action'
)
- @HasPermissionAllDecorator('hg.admin')
- def lock(self, apiuser, repoid, userid, locked):
+ def lock(self, apiuser, repoid, locked, userid=Optional(OAttr('apiuser'))):
"""
- Set locking state on particular repository by given user
+ Set locking state on particular repository by given user, if
+ this command is runned by non-admin account userid is set to user
+ who is calling this method
:param apiuser:
:param repoid:
@@ -195,6 +214,22 @@ class ApiController(JSONRPCController):
:param locked:
"""
repo = get_repo_or_error(repoid)
+ if HasPermissionAnyApi('hg.admin')(user=apiuser):
+ pass
+ elif HasRepoPermissionAnyApi('repository.admin',
+ 'repository.write')(user=apiuser,
+ repo_name=repo.repo_name):
+ #make sure normal user does not pass someone else userid,
+ #he is not allowed to do that
+ if not isinstance(userid, Optional) and userid != apiuser.user_id:
+ raise JSONRPCError(
+ 'userid is not the same as your user'
+ )
+ else:
+ raise JSONRPCError('repository `%s` does not exist' % (repoid))
+
+ if isinstance(userid, Optional):
+ userid = apiuser.user_id
user = get_user_or_error(userid)
locked = bool(locked)
try:
@@ -212,13 +247,38 @@ class ApiController(JSONRPCController):
)
@HasPermissionAllDecorator('hg.admin')
- def get_user(self, apiuser, userid):
+ def show_ip(self, apiuser, userid):
+ """
+ Shows IP address as seen from RhodeCode server, together with all
+ defined IP addresses for given user
+
+ :param apiuser:
+ :param userid:
+ """
+ user = get_user_or_error(userid)
+ ips = UserIpMap.query().filter(UserIpMap.user == user).all()
+ return dict(
+ ip_addr_server=self.ip_addr,
+ user_ips=ips
+ )
+
+ def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
""""
- Get a user by username
+ Get a user by username, or userid, if userid is given
:param apiuser:
:param userid:
"""
+ if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+ #make sure normal user does not pass someone else userid,
+ #he is not allowed to do that
+ if not isinstance(userid, Optional) and userid != apiuser.user_id:
+ raise JSONRPCError(
+ 'userid is not the same as your user'
+ )
+
+ if isinstance(userid, Optional):
+ userid = apiuser.user_id
user = get_user_or_error(userid)
data = user.get_api_data()
@@ -479,7 +539,6 @@ class ApiController(JSONRPCController):
)
)
- @HasPermissionAnyDecorator('hg.admin')
def get_repo(self, apiuser, repoid):
""""
Get repository by name
@@ -489,6 +548,12 @@ class ApiController(JSONRPCController):
"""
repo = get_repo_or_error(repoid)
+ if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+ # check if we have admin permission for this repo !
+ if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
+ repo_name=repo.repo_name) is False:
+ raise JSONRPCError('repository `%s` does not exist' % (repoid))
+
members = []
for user in repo.repo_to_perm:
perm = user.permission.permission_name
@@ -510,20 +575,23 @@ class ApiController(JSONRPCController):
data['members'] = members
return data
- @HasPermissionAnyDecorator('hg.admin')
def get_repos(self, apiuser):
""""
Get all repositories
:param apiuser:
"""
-
result = []
- for repo in RepoModel().get_all():
+ if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+ repos = RepoModel().get_all_user_repos(user=apiuser)
+ else:
+ repos = RepoModel().get_all()
+
+ for repo in repos:
result.append(repo.get_api_data())
return result
- @HasPermissionAnyDecorator('hg.admin')
+ @HasPermissionAllDecorator('hg.admin')
def get_repo_nodes(self, apiuser, repoid, revision, root_path,
ret_type='all'):
"""
@@ -556,12 +624,16 @@ class ApiController(JSONRPCController):
)
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
- def create_repo(self, apiuser, repo_name, owner, repo_type,
+ def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
+ repo_type=Optional('hg'),
description=Optional(''), private=Optional(False),
- clone_uri=Optional(None), landing_rev=Optional('tip')):
+ clone_uri=Optional(None), landing_rev=Optional('tip'),
+ enable_statistics=Optional(False),
+ enable_locking=Optional(False),
+ enable_downloads=Optional(False)):
"""
Create repository, if clone_url is given it makes a remote clone
- if repo_name is withina group name the groups will be created
+ if repo_name is within a group name the groups will be created
automatically if they aren't present
:param apiuser:
@@ -573,12 +645,32 @@ class ApiController(JSONRPCController):
:param clone_uri:
:param landing_rev:
"""
+ if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+ if not isinstance(owner, Optional):
+ #forbid setting owner for non-admins
+ raise JSONRPCError(
+ 'Only RhodeCode admin can specify `owner` param'
+ )
+ if isinstance(owner, Optional):
+ owner = apiuser.user_id
+
owner = get_user_or_error(owner)
if RepoModel().get_by_repo_name(repo_name):
raise JSONRPCError("repo `%s` already exist" % repo_name)
- private = Optional.extract(private)
+ defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
+ if isinstance(private, Optional):
+ private = defs.get('repo_private') or Optional.extract(private)
+ if isinstance(repo_type, Optional):
+ repo_type = defs.get('repo_type')
+ if isinstance(enable_statistics, Optional):
+ enable_statistics = defs.get('repo_enable_statistics')
+ if isinstance(enable_locking, Optional):
+ enable_locking = defs.get('repo_enable_locking')
+ if isinstance(enable_downloads, Optional):
+ enable_downloads = defs.get('repo_enable_downloads')
+
clone_uri = Optional.extract(clone_uri)
description = Optional.extract(description)
landing_rev = Optional.extract(landing_rev)
@@ -596,32 +688,51 @@ class ApiController(JSONRPCController):
clone_uri=clone_uri,
repos_group=group,
landing_rev=landing_rev,
+ enable_statistics=enable_statistics,
+ enable_downloads=enable_downloads,
+ enable_locking=enable_locking
)
Session().commit()
-
return dict(
msg="Created new repository `%s`" % (repo.repo_name),
repo=repo.get_api_data()
)
-
except Exception:
log.error(traceback.format_exc())
raise JSONRPCError('failed to create repository `%s`' % repo_name)
- @HasPermissionAnyDecorator('hg.admin')
- def fork_repo(self, apiuser, repoid, fork_name, owner,
+ @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
+ def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
description=Optional(''), copy_permissions=Optional(False),
private=Optional(False), landing_rev=Optional('tip')):
repo = get_repo_or_error(repoid)
repo_name = repo.repo_name
- owner = get_user_or_error(owner)
_repo = RepoModel().get_by_repo_name(fork_name)
if _repo:
type_ = 'fork' if _repo.fork else 'repo'
raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
+ if HasPermissionAnyApi('hg.admin')(user=apiuser):
+ pass
+ elif HasRepoPermissionAnyApi('repository.admin',
+ 'repository.write',
+ 'repository.read')(user=apiuser,
+ repo_name=repo.repo_name):
+ if not isinstance(owner, Optional):
+ #forbid setting owner for non-admins
+ raise JSONRPCError(
+ 'Only RhodeCode admin can specify `owner` param'
+ )
+ else:
+ raise JSONRPCError('repository `%s` does not exist' % (repoid))
+
+ if isinstance(owner, Optional):
+ owner = apiuser.user_id
+
+ owner = get_user_or_error(owner)
+
try:
# create structure of groups and return the last group
group = map_groups(fork_name)
@@ -652,7 +763,6 @@ class ApiController(JSONRPCController):
fork_name)
)
- @HasPermissionAnyDecorator('hg.admin')
def delete_repo(self, apiuser, repoid):
"""
Deletes a given repository
@@ -662,6 +772,12 @@ class ApiController(JSONRPCController):
"""
repo = get_repo_or_error(repoid)
+ if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+ # check if we have admin permission for this repo !
+ if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
+ repo_name=repo.repo_name) is False:
+ raise JSONRPCError('repository `%s` does not exist' % (repoid))
+
try:
RepoModel().delete(repo)
Session().commit()
@@ -675,7 +791,7 @@ class ApiController(JSONRPCController):
'failed to delete repository `%s`' % repo.repo_name
)
- @HasPermissionAnyDecorator('hg.admin')
+ @HasPermissionAllDecorator('hg.admin')
def grant_user_permission(self, apiuser, repoid, userid, perm):
"""
Grant permission for user on given repository, or update existing one
@@ -708,7 +824,7 @@ class ApiController(JSONRPCController):
)
)
- @HasPermissionAnyDecorator('hg.admin')
+ @HasPermissionAllDecorator('hg.admin')
def revoke_user_permission(self, apiuser, repoid, userid):
"""
Revoke permission for user on given repository
@@ -739,7 +855,7 @@ class ApiController(JSONRPCController):
)
)
- @HasPermissionAnyDecorator('hg.admin')
+ @HasPermissionAllDecorator('hg.admin')
def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
perm):
"""
@@ -778,7 +894,7 @@ class ApiController(JSONRPCController):
)
)
- @HasPermissionAnyDecorator('hg.admin')
+ @HasPermissionAllDecorator('hg.admin')
def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
"""
Revoke permission for users group on given repository