@@ -218,7 +218,7 @@ 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::
@@ -574,8 +574,9 @@ 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.
@@ -637,8 +638,9 @@ OUTPUT::
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.
@@ -709,11 +711,12 @@ 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
@@ -723,7 +726,7 @@ INPUT::
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)",
@@ -761,10 +764,11 @@ 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.
@@ -775,7 +779,7 @@ INPUT::
"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>",
@@ -796,8 +800,8 @@ 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.
@@ -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.auth import AuthUser
from rhodecode.lib.base import _get_ip_addr, _get_access_path
from rhodecode.lib.utils2 import safe_unicode
@@ -219,13 +219,15 @@ class ApiController(JSONRPCController):
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)
@@ -267,13 +269,15 @@ class ApiController(JSONRPCController):
:param apiuser:
:param userid:
"""
if HasPermissionAnyApi('hg.admin')(user=apiuser):
pass
if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
'Only RhodeCode admin can specify `userid` params'
@@ -535,7 +539,6 @@ class ApiController(JSONRPCController):
@HasPermissionAllDecorator('hg.admin')
def get_repo(self, apiuser, repoid):
""""
Get repository by name
@@ -545,6 +548,12 @@ class ApiController(JSONRPCController):
repo = get_repo_or_error(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
@@ -566,16 +575,19 @@ class ApiController(JSONRPCController):
data['members'] = members
return data
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
@@ -612,7 +624,8 @@ class ApiController(JSONRPCController):
@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),
@@ -620,7 +633,7 @@ class ApiController(JSONRPCController):
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
@@ -632,6 +645,15 @@ class ApiController(JSONRPCController):
: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):
@@ -672,29 +694,45 @@ class ApiController(JSONRPCController):
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)
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,
try:
# create structure of groups and return the last group
group = map_groups(fork_name)
@@ -725,7 +763,6 @@ class ApiController(JSONRPCController):
fork_name)
def delete_repo(self, apiuser, repoid):
Deletes a given repository
@@ -735,6 +772,12 @@ class ApiController(JSONRPCController):
RepoModel().delete(repo)
@@ -90,6 +90,22 @@ class RepoModel(BaseModel):
"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([
@@ -59,13 +59,13 @@ def destroy_users_group(name=TEST_USERS_
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
@@ -93,7 +93,7 @@ class BaseTestApi(object):
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',
@@ -101,7 +101,8 @@ class BaseTestApi(object):
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
@classmethod
def teardownClass(self):
@@ -148,10 +149,10 @@ class BaseTestApi(object):
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')
response = api_call(self, params)
expected = 'Missing non optional `userid` arg in JSON DATA'
expected = 'Missing non optional `repoid` arg in JSON DATA'
def test_api_get_users(self):
@@ -184,6 +185,36 @@ class BaseTestApi(object):
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 !
@@ -237,6 +268,42 @@ class BaseTestApi(object):
% (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,
locked=True)
expected = ('User `%s` set lock state for repo `%s` to `%s`'
% (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',
@@ -466,6 +533,48 @@ class BaseTestApi(object):
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 = user.user
user_data = user.get_api_data()
user_data['type'] = "user"
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
members.append(users_group_data)
ret['members'] = members
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')
@@ -487,6 +596,18 @@ class BaseTestApi(object):
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):
ret = jsonify(result)
@parameterized.expand([('all', 'all'),
('dirs', 'dirs'),
('files', 'files'), ])
@@ -569,6 +690,56 @@ class BaseTestApi(object):
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_type='hg',
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
@@ -607,6 +778,35 @@ class BaseTestApi(object):
def test_api_delete_repo_by_non_admin(self):
id_, params = _build_data(self.apikey_regular, 'delete_repo',
repoid=repo_name,)
'msg': 'Deleted repository `%s`' % repo_name,
'success': True
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):
@@ -639,6 +839,49 @@ 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):
owner=TEST_USER_ADMIN_LOGIN,
def test_api_fork_repo_non_admin_no_permission_to_fork(self):
def test_api_fork_repo_unknown_owner(self):
Status change: