@@ -215,13 +215,13 @@ OUTPUT::
get_user
--------
Get's an user by username or user_id, Returns empty result if user is not found.
If userid param is skipped it is set to id of user who is calling this method.
This command can be executed only using api_key belonging to user with admin
rights, or regular users which cannot specify userid parameter.
rights, or regular users that cannot specify different userid than theirs
INPUT::
id : <id_for_response>
api_key : "<api_key>"
@@ -571,14 +571,15 @@ OUTPUT::
get_repo
Gets an existing repository by it's name or repository_id. Members will return
either users_group or user associated to that repository. This command can
be executed only using api_key belonging to user with admin rights.
either users_group or user associated to that repository. This command can be
executed only using api_key belonging to user with admin
rights or regular user that have at least read access to repository.
@@ -634,14 +635,15 @@ OUTPUT::
error: null
get_repos
---------
Lists all existing repositories. This command can be executed only using api_key
belonging to user with admin rights
Lists all existing repositories. This command can be executed only using
api_key belonging to user with admin rights or regular user that have
admin, write or read access to repository.
@@ -706,27 +708,28 @@ OUTPUT::
create_repo
-----------
Creates a repository. This command can be executed only using api_key
belonging to user with admin rights.
If repository name contains "/", all needed repository groups will be created.
For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
and create "baz" repository with "bar" as group.
Creates a repository. If repository name contains "/", all needed repository
groups will be created. For example "foo/bar/baz" will create groups
"foo", "bar" (with "foo" as parent), and create "baz" repository with
"bar" as group. This command can be executed only using api_key belonging to user with admin
rights or regular user that have create repository permission. Regular users
cannot specify owner parameter
method : "create_repo"
args: {
"repo_name" : "<reponame>",
"owner" : "<onwer_name_or_id>",
"owner" : "<onwer_name_or_id = Optional(=apiuser)>",
"repo_type" : "<repo_type> = Optional('hg')",
"description" : "<description> = Optional('')",
"private" : "<bool> = Optional(False)",
"clone_uri" : "<clone_uri> = Optional(None)",
"landing_rev" : "<landing_rev> = Optional('tip')",
"enable_downloads": "<bool> = Optional(False)",
@@ -758,27 +761,28 @@ OUTPUT::
fork_repo
Creates a fork of given repo. This command can be executed only using api_key
belonging to user with admin rights. In case of using celery this will
Creates a fork of given repo. In case of using celery this will
immidiatelly return success message, while fork is going to be created
asynchronous
asynchronous. This command can be executed only using api_key belonging to
user with admin rights or regular user that have fork permission, and at least
read access to forking repository. Regular users cannot specify owner parameter.
method : "fork_repo"
"repoid" : "<reponame or repo_id>",
"fork_name": "<forkname>",
"owner": "<username or user_id>",
"owner": "<username or user_id = Optional(=apiuser)>",
"description": "<description>",
"copy_permissions": "<bool>",
"private": "<bool>",
"landing_rev": "<landing_rev>"
}
@@ -793,14 +797,14 @@ OUTPUT::
delete_repo
Deletes a repository. This command can be executed only using api_key
Deletes a repository. This command can be executed only using api_key belonging to user with admin
rights or regular user that have admin access to repository.
@@ -29,23 +29,21 @@ import inspect
import logging
import types
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.auth import AuthUser
from rhodecode.lib.base import _get_ip_addr, _get_access_path
from rhodecode.lib.utils2 import safe_unicode
log = logging.getLogger('JSONRPC')
@@ -216,19 +216,21 @@ class ApiController(JSONRPCController):
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 userid, he is not allowed to do that
if not isinstance(userid, Optional):
#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(
'Only RhodeCode admin can specify `userid` param'
'userid is not the same as your user'
)
else:
return abort(403)
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:
if locked:
@@ -264,19 +266,21 @@ class ApiController(JSONRPCController):
""""
Get a user by username, or userid, if userid is given
:param apiuser:
:param userid:
"""
if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
'Only RhodeCode admin can specify `userid` params'
data = user.get_api_data()
data['permissions'] = AuthUser(user_id=user.user_id).permissions
return data
@@ -532,22 +536,27 @@ class ApiController(JSONRPCController):
'failed to remove member from users group `%s`' % (
users_group.users_group_name
@HasPermissionAllDecorator('hg.admin')
def get_repo(self, apiuser, repoid):
Get repository by name
:param repoid:
# check if we have admin permission for this repo !
if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
repo_name=repo.repo_name) is False:
members = []
for user in repo.repo_to_perm:
perm = user.permission.permission_name
user = user.user
user_data = user.get_api_data()
user_data['type'] = "user"
@@ -563,22 +572,25 @@ class ApiController(JSONRPCController):
members.append(users_group_data)
data = repo.get_api_data()
data['members'] = members
def get_repos(self, apiuser):
Get all repositories
result = []
repos = RepoModel().get_all_user_repos(user=apiuser)
repos = RepoModel().get_all()
for repo in RepoModel().get_all():
for repo in repos:
result.append(repo.get_api_data())
return result
def get_repo_nodes(self, apiuser, repoid, revision, root_path,
ret_type='all'):
@@ -609,32 +621,42 @@ class ApiController(JSONRPCController):
log.error(traceback.format_exc())
'failed to get repo: `%s` nodes' % repo.repo_name
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
def create_repo(self, apiuser, repo_name, owner, repo_type=Optional('hg'),
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'),
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 repo_name:
:param onwer:
:param repo_type:
:param description:
:param private:
:param clone_uri:
:param landing_rev:
if not isinstance(owner, Optional):
#forbid setting owner for non-admins
'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)
defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
@@ -669,35 +691,51 @@ class ApiController(JSONRPCController):
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:
raise JSONRPCError('failed to create repository `%s`' % repo_name)
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_name = repo.repo_name
_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))
'repository.write',
'repository.read')(user=apiuser,
# create structure of groups and return the last group
group = map_groups(fork_name)
form_data = dict(
repo_name=fork_name,
@@ -722,22 +760,27 @@ class ApiController(JSONRPCController):
'failed to fork repository `%s` as `%s`' % (repo_name,
fork_name)
def delete_repo(self, apiuser, repoid):
Deletes a given repository
RepoModel().delete(repo)
msg='Deleted repository `%s`' % repo.repo_name,
success=True
@@ -87,12 +87,28 @@ class RepoModel(BaseModel):
if cache:
repo = repo.options(FromCache("sql_cache_short",
"get_repo_%s" % repo_name))
return repo.scalar()
def get_all_user_repos(self, user):
Get's all repositories that user have at least read access
:param user:
:type user:
user = self._get_user(user)
repos = AuthUser(user_id=user.user_id).permissions['repositories']
access_check = lambda r: r[1] in ['repository.read',
'repository.admin']
repos = [x[0] for x in filter(access_check, repos.items())]
return Repository.query().filter(Repository.repo_name.in_(repos))
def get_users_js(self):
users = self.sa.query(User).filter(User.active == True).all()
return json.dumps([
{
'id': u.user_id,
'fname': u.name,
@@ -56,19 +56,19 @@ def make_users_group(name=TEST_USERS_GRO
def destroy_users_group(name=TEST_USERS_GROUP):
UsersGroupModel().delete(users_group=name, force=True)
def create_repo(repo_name, repo_type):
def create_repo(repo_name, repo_type, owner=None):
# create new repo
form_data = _get_repo_create_params(
repo_name_full=repo_name,
repo_description='description %s' % repo_name,
cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
cur_user = UserModel().get_by_username(owner or TEST_USER_ADMIN_LOGIN)
r = RepoModel().create(form_data, cur_user)
return r
def create_fork(fork_name, fork_type, fork_of):
@@ -90,21 +90,22 @@ class BaseTestApi(object):
REPO_TYPE = None
@classmethod
def setUpClass(self):
self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
self.apikey = self.usr.api_key
self.TEST_USER = UserModel().create_or_update(
self.test_user = UserModel().create_or_update(
username='test-api',
password='test',
email='test@api.rhodecode.org',
firstname='first',
lastname='last'
self.TEST_USER_LOGIN = self.TEST_USER.username
self.TEST_USER_LOGIN = self.test_user.username
self.apikey_regular = self.test_user.api_key
def teardownClass(self):
def setUp(self):
@@ -145,16 +146,16 @@ class BaseTestApi(object):
response = api_call(self, params)
expected = 'Invalid API KEY'
self._compare_error(id_, expected, given=response.body)
def test_api_missing_non_optional_param(self):
id_, params = _build_data(self.apikey, 'get_user')
id_, params = _build_data(self.apikey, 'get_repo')
expected = 'Missing non optional `userid` arg in JSON DATA'
expected = 'Missing non optional `repoid` arg in JSON DATA'
def test_api_get_users(self):
id_, params = _build_data(self.apikey, 'get_users',)
ret_all = []
@@ -181,12 +182,42 @@ class BaseTestApi(object):
userid='trololo')
expected = "user `%s` does not exist" % 'trololo'
def test_api_get_user_without_giving_userid(self):
usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
ret = usr.get_api_data()
ret['permissions'] = AuthUser(usr.user_id).permissions
expected = ret
self._compare_ok(id_, expected, given=response.body)
def test_api_get_user_without_giving_userid_non_admin(self):
id_, params = _build_data(self.apikey_regular, 'get_user')
usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
def test_api_get_user_with_giving_userid_non_admin(self):
id_, params = _build_data(self.apikey_regular, 'get_user',
userid=self.TEST_USER_LOGIN)
expected = 'userid is not the same as your user'
def test_api_pull(self):
#TODO: issues with rhodecode_extras here.. not sure why !
# repo_name = 'test_pull'
# r = create_repo(repo_name, self.REPO_TYPE)
@@ -234,12 +265,48 @@ class BaseTestApi(object):
locked=True)
expected = ('User `%s` set lock state for repo `%s` to `%s`'
% (TEST_USER_ADMIN_LOGIN, self.REPO, True))
def test_api_lock_repo_lock_aquire_by_non_admin(self):
repo_name = 'api_delete_me'
create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
id_, params = _build_data(self.apikey_regular, 'lock',
repoid=repo_name,
% (self.TEST_USER_LOGIN, repo_name, True))
finally:
destroy_repo(repo_name)
def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
userid=TEST_USER_ADMIN_LOGIN,
def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
repoid=self.REPO,
expected = 'repository `%s` does not exist' % (self.REPO)
def test_api_lock_repo_lock_release(self):
id_, params = _build_data(self.apikey, 'lock',
locked=False)
@@ -463,12 +530,54 @@ class BaseTestApi(object):
ret['members'] = members
destroy_users_group(new_group)
def test_api_get_repo_by_non_admin(self):
id_, params = _build_data(self.apikey, 'get_repo',
repoid=self.REPO)
repo = RepoModel().get_by_repo_name(self.REPO)
ret = repo.get_api_data()
user_data['permission'] = perm
members.append(user_data)
for users_group in repo.users_group_to_perm:
perm = users_group.permission.permission_name
users_group = users_group.users_group
users_group_data = users_group.get_api_data()
users_group_data['type'] = "users_group"
users_group_data['permission'] = perm
def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
RepoModel().grant_user_permission(repo=self.REPO,
user=self.TEST_USER_LOGIN,
perm='repository.none')
id_, params = _build_data(self.apikey_regular, 'get_repo',
def test_api_get_repo_that_doesn_not_exist(self):
repoid='no-such-repo')
ret = 'repository `%s` does not exist' % 'no-such-repo'
@@ -484,12 +593,24 @@ class BaseTestApi(object):
ret = jsonify(result)
def test_api_get_repos_non_admin(self):
id_, params = _build_data(self.apikey_regular, 'get_repos')
for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
@parameterized.expand([('all', 'all'),
('dirs', 'dirs'),
('files', 'files'), ])
def test_api_get_repo_nodes(self, name, ret_type):
rev = 'tip'
path = '/'
@@ -566,12 +687,62 @@ class BaseTestApi(object):
repo_type='hg',
expected = 'user `%s` does not exist' % owner
def test_api_create_repo_dont_specify_owner(self):
repo_name = 'api-repo'
owner = 'i-dont-exist'
id_, params = _build_data(self.apikey, 'create_repo',
repo_name=repo_name,
repo = RepoModel().get_by_repo_name(repo_name)
ret = {
'msg': 'Created new repository `%s`' % repo_name,
'repo': jsonify(repo.get_api_data())
def test_api_create_repo_by_non_admin(self):
id_, params = _build_data(self.apikey_regular, 'create_repo',
def test_api_create_repo_by_non_admin_specify_owner(self):
owner=owner
expected = 'Only RhodeCode admin can specify `owner` param'
def test_api_create_repo_exists(self):
repo_name = self.REPO
owner=TEST_USER_ADMIN_LOGIN,
@@ -604,12 +775,41 @@ class BaseTestApi(object):
'msg': 'Deleted repository `%s`' % repo_name,
'success': True
def test_api_delete_repo_by_non_admin(self):
id_, params = _build_data(self.apikey_regular, 'delete_repo',
repoid=repo_name,)
def test_api_delete_repo_by_non_admin_no_permission(self):
create_repo(repo_name, self.REPO_TYPE)
expected = 'repository `%s` does not exist' % (repo_name)
def test_api_delete_repo_exception_occurred(self):
with mock.patch.object(RepoModel, 'delete', crash):
id_, params = _build_data(self.apikey, 'delete_repo',
@@ -636,12 +836,55 @@ class BaseTestApi(object):
destroy_repo(fork_name)
def test_api_fork_repo_non_admin(self):
fork_name = 'api-repo-fork'
id_, params = _build_data(self.apikey_regular, 'fork_repo',
fork_name=fork_name,
'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
fork_name),
def test_api_fork_repo_non_admin_specify_owner(self):
def test_api_fork_repo_non_admin_no_permission_to_fork(self):
def test_api_fork_repo_unknown_owner(self):
id_, params = _build_data(self.apikey, 'fork_repo',
Status change: