changeset 3163:28571535dd61 beta

API methods create_repo, fork_repo, delete_repo, get_repo, get_repos can be executed by non-admin users ref #539
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 14 Jan 2013 00:38:24 +0100
parents a0a8f38e8fb8
children 01cb7df198ae
files docs/api/api.rst rhodecode/controllers/api/__init__.py rhodecode/controllers/api/api.py rhodecode/model/repo.py rhodecode/tests/api/api_base.py
diffstat 5 files changed, 352 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/docs/api/api.rst	Sun Jan 13 23:11:55 2013 +0100
+++ b/docs/api/api.rst	Mon Jan 14 00:38:24 2013 +0100
@@ -218,7 +218,7 @@
 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 @@
 --------
 
 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.
 
 
 INPUT::
@@ -637,8 +638,9 @@
 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.
 
 
 INPUT::
@@ -709,11 +711,12 @@
 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
 
 
 INPUT::
@@ -723,7 +726,7 @@
     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 @@
 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.
 
 
 INPUT::
@@ -775,7 +779,7 @@
     args:     {
                 "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 @@
 delete_repo
 -----------
 
-Deletes a repository. This command can be executed only using api_key
-belonging to user with admin rights.
+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.
 
 
 INPUT::
--- a/rhodecode/controllers/api/__init__.py	Sun Jan 13 23:11:55 2013 +0100
+++ b/rhodecode/controllers/api/__init__.py	Mon Jan 14 00:38:24 2013 +0100
@@ -32,17 +32,15 @@
 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
--- a/rhodecode/controllers/api/api.py	Sun Jan 13 23:11:55 2013 +0100
+++ b/rhodecode/controllers/api/api.py	Mon Jan 14 00:38:24 2013 +0100
@@ -219,13 +219,15 @@
         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 @@
         :param apiuser:
         :param userid:
         """
-        if HasPermissionAnyApi('hg.admin')(user=apiuser):
-            pass
-        else:
-            if not isinstance(userid, Optional):
+        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(
-                    'Only RhodeCode admin can specify `userid` params'
+                    'userid is not the same as your user'
                 )
+
+        if isinstance(userid, Optional):
             userid = apiuser.user_id
 
         user = get_user_or_error(userid)
@@ -535,7 +539,6 @@
                     )
             )
 
-    @HasPermissionAllDecorator('hg.admin')
     def get_repo(self, apiuser, repoid):
         """"
         Get repository by name
@@ -545,6 +548,12 @@
         """
         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
@@ -566,16 +575,19 @@
         data['members'] = members
         return data
 
-    @HasPermissionAllDecorator('hg.admin')
     def get_repos(self, apiuser):
         """"
         Get all repositories
 
         :param apiuser:
         """
+        result = []
+        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+            repos = RepoModel().get_all_user_repos(user=apiuser)
+        else:
+            repos = RepoModel().get_all()
 
-        result = []
-        for repo in RepoModel().get_all():
+        for repo in repos:
             result.append(repo.get_api_data())
         return result
 
@@ -612,7 +624,8 @@
             )
 
     @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 @@
                     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:
@@ -632,6 +645,15 @@
         :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):
@@ -672,29 +694,45 @@
             )
 
             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)
 
-    @HasPermissionAllDecorator('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)
@@ -725,7 +763,6 @@
                                                             fork_name)
             )
 
-    @HasPermissionAllDecorator('hg.admin')
     def delete_repo(self, apiuser, repoid):
         """
         Deletes a given repository
@@ -735,6 +772,12 @@
         """
         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()
--- a/rhodecode/model/repo.py	Sun Jan 13 23:11:55 2013 +0100
+++ b/rhodecode/model/repo.py	Mon Jan 14 00:38:24 2013 +0100
@@ -90,6 +90,22 @@
                                           "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:
+        """
+        from rhodecode.lib.auth import AuthUser
+        user = self._get_user(user)
+        repos = AuthUser(user_id=user.user_id).permissions['repositories']
+        access_check = lambda r: r[1] in ['repository.read',
+                                          'repository.write',
+                                          '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([
--- a/rhodecode/tests/api/api_base.py	Sun Jan 13 23:11:55 2013 +0100
+++ b/rhodecode/tests/api/api_base.py	Mon Jan 14 00:38:24 2013 +0100
@@ -59,13 +59,13 @@
     Session().commit()
 
 
-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)
     Session().commit()
     return r
@@ -93,7 +93,7 @@
     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 @@
             lastname='last'
         )
         Session().commit()
-        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 @@
         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'
         self._compare_error(id_, expected, given=response.body)
 
     def test_api_get_users(self):
@@ -184,6 +185,36 @@
         expected = "user `%s` does not exist" % 'trololo'
         self._compare_error(id_, expected, given=response.body)
 
+    def test_api_get_user_without_giving_userid(self):
+        id_, params = _build_data(self.apikey, 'get_user')
+        response = api_call(self, params)
+
+        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')
+        response = api_call(self, params)
+
+        usr = UserModel().get_by_username(self.TEST_USER_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_with_giving_userid_non_admin(self):
+        id_, params = _build_data(self.apikey_regular, 'get_user',
+                                  userid=self.TEST_USER_LOGIN)
+        response = api_call(self, params)
+
+        expected = 'userid is not the same as your user'
+        self._compare_error(id_, expected, given=response.body)
+
     def test_api_pull(self):
         #TODO: issues with rhodecode_extras here.. not sure why !
         pass
@@ -237,6 +268,42 @@
                    % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
         self._compare_ok(id_, expected, given=response.body)
 
+    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)
+        try:
+            id_, params = _build_data(self.apikey_regular, 'lock',
+                                      repoid=repo_name,
+                                      locked=True)
+            response = api_call(self, params)
+            expected = ('User `%s` set lock state for repo `%s` to `%s`'
+                       % (self.TEST_USER_LOGIN, repo_name, True))
+            self._compare_ok(id_, expected, given=response.body)
+        finally:
+            destroy_repo(repo_name)
+
+    def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
+        repo_name = 'api_delete_me'
+        create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
+        try:
+            id_, params = _build_data(self.apikey_regular, 'lock',
+                                      userid=TEST_USER_ADMIN_LOGIN,
+                                      repoid=repo_name,
+                                      locked=True)
+            response = api_call(self, params)
+            expected = 'userid is not the same as your user'
+            self._compare_error(id_, expected, given=response.body)
+        finally:
+            destroy_repo(repo_name)
+
+    def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
+        id_, params = _build_data(self.apikey_regular, 'lock',
+                                  repoid=self.REPO,
+                                  locked=True)
+        response = api_call(self, params)
+        expected = 'repository `%s` does not exist' % (self.REPO)
+        self._compare_error(id_, expected, given=response.body)
+
     def test_api_lock_repo_lock_release(self):
         id_, params = _build_data(self.apikey, 'lock',
                                   userid=TEST_USER_ADMIN_LOGIN,
@@ -466,6 +533,48 @@
         self._compare_ok(id_, expected, given=response.body)
         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)
+        response = api_call(self, params)
+
+        repo = RepoModel().get_by_repo_name(self.REPO)
+        ret = repo.get_api_data()
+
+        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"
+            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
+
+        expected = ret
+        self._compare_ok(id_, expected, given=response.body)
+
+    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',
+                                  repoid=self.REPO)
+        response = api_call(self, params)
+
+        expected = 'repository `%s` does not exist' % (self.REPO)
+        self._compare_error(id_, expected, given=response.body)
+
     def test_api_get_repo_that_doesn_not_exist(self):
         id_, params = _build_data(self.apikey, 'get_repo',
                                   repoid='no-such-repo')
@@ -487,6 +596,18 @@
         expected = ret
         self._compare_ok(id_, expected, given=response.body)
 
+    def test_api_get_repos_non_admin(self):
+        id_, params = _build_data(self.apikey_regular, 'get_repos')
+        response = api_call(self, params)
+
+        result = []
+        for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
+            result.append(repo.get_api_data())
+        ret = jsonify(result)
+
+        expected = ret
+        self._compare_ok(id_, expected, given=response.body)
+
     @parameterized.expand([('all', 'all'),
                            ('dirs', 'dirs'),
                            ('files', 'files'), ])
@@ -569,6 +690,56 @@
         expected = 'user `%s` does not exist' % owner
         self._compare_error(id_, expected, given=response.body)
 
+    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',
+                                  )
+        response = api_call(self, params)
+
+        repo = RepoModel().get_by_repo_name(repo_name)
+        ret = {
+            'msg': 'Created new repository `%s`' % repo_name,
+            'repo': jsonify(repo.get_api_data())
+        }
+        expected = ret
+        self._compare_ok(id_, expected, given=response.body)
+        destroy_repo(repo_name)
+
+    def test_api_create_repo_by_non_admin(self):
+        repo_name = 'api-repo'
+        owner = 'i-dont-exist'
+        id_, params = _build_data(self.apikey_regular, 'create_repo',
+                                    repo_name=repo_name,
+                                    repo_type='hg',
+                                  )
+        response = api_call(self, params)
+
+        repo = RepoModel().get_by_repo_name(repo_name)
+        ret = {
+            'msg': 'Created new repository `%s`' % repo_name,
+            'repo': jsonify(repo.get_api_data())
+        }
+        expected = ret
+        self._compare_ok(id_, expected, given=response.body)
+        destroy_repo(repo_name)
+
+    def test_api_create_repo_by_non_admin_specify_owner(self):
+        repo_name = 'api-repo'
+        owner = 'i-dont-exist'
+        id_, params = _build_data(self.apikey_regular, 'create_repo',
+                                    repo_name=repo_name,
+                                    repo_type='hg',
+                                    owner=owner
+                                  )
+        response = api_call(self, params)
+
+        expected = 'Only RhodeCode admin can specify `owner` param'
+        self._compare_error(id_, expected, given=response.body)
+        destroy_repo(repo_name)
+
     def test_api_create_repo_exists(self):
         repo_name = self.REPO
         id_, params = _build_data(self.apikey, 'create_repo',
@@ -607,6 +778,35 @@
         expected = ret
         self._compare_ok(id_, expected, given=response.body)
 
+    def test_api_delete_repo_by_non_admin(self):
+        repo_name = 'api_delete_me'
+        create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
+        try:
+            id_, params = _build_data(self.apikey_regular, 'delete_repo',
+                                      repoid=repo_name,)
+            response = api_call(self, params)
+
+            ret = {
+                'msg': 'Deleted repository `%s`' % repo_name,
+                'success': True
+            }
+            expected = ret
+            self._compare_ok(id_, expected, given=response.body)
+        finally:
+            destroy_repo(repo_name)
+
+    def test_api_delete_repo_by_non_admin_no_permission(self):
+        repo_name = 'api_delete_me'
+        create_repo(repo_name, self.REPO_TYPE)
+        try:
+            id_, params = _build_data(self.apikey_regular, 'delete_repo',
+                                      repoid=repo_name,)
+            response = api_call(self, params)
+            expected = 'repository `%s` does not exist' % (repo_name)
+            self._compare_error(id_, expected, given=response.body)
+        finally:
+            destroy_repo(repo_name)
+
     def test_api_delete_repo_exception_occurred(self):
         repo_name = 'api_delete_me'
         create_repo(repo_name, self.REPO_TYPE)
@@ -639,6 +839,49 @@
         self._compare_ok(id_, expected, given=response.body)
         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',
+                                    repoid=self.REPO,
+                                    fork_name=fork_name,
+                                  )
+        response = api_call(self, params)
+
+        ret = {
+            'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
+                                                     fork_name),
+            'success': True
+        }
+        expected = ret
+        self._compare_ok(id_, expected, given=response.body)
+        destroy_repo(fork_name)
+
+    def test_api_fork_repo_non_admin_specify_owner(self):
+        fork_name = 'api-repo-fork'
+        id_, params = _build_data(self.apikey_regular, 'fork_repo',
+                                    repoid=self.REPO,
+                                    fork_name=fork_name,
+                                    owner=TEST_USER_ADMIN_LOGIN,
+                                  )
+        response = api_call(self, params)
+        expected = 'Only RhodeCode admin can specify `owner` param'
+        self._compare_error(id_, expected, given=response.body)
+        destroy_repo(fork_name)
+
+    def test_api_fork_repo_non_admin_no_permission_to_fork(self):
+        RepoModel().grant_user_permission(repo=self.REPO,
+                                          user=self.TEST_USER_LOGIN,
+                                          perm='repository.none')
+        fork_name = 'api-repo-fork'
+        id_, params = _build_data(self.apikey_regular, 'fork_repo',
+                                    repoid=self.REPO,
+                                    fork_name=fork_name,
+                                  )
+        response = api_call(self, params)
+        expected = 'repository `%s` does not exist' % (self.REPO)
+        self._compare_error(id_, expected, given=response.body)
+        destroy_repo(fork_name)
+
     def test_api_fork_repo_unknown_owner(self):
         fork_name = 'api-repo-fork'
         owner = 'i-dont-exist'