changeset 6511:a17c8e5f6712

auth: simplify repository permission checks In practice, Kallithea has the 'repository.admin' permission imply the 'repository.write' permission, which again implies 'repository.read'. This codifies/enforces this practice by replacing HasRepoPermissionAny "perm function" with the new HasRepositoryLevel function, reducing the risk of errors and saving quite a lot of typing.
author Søren Løvborg <sorenl@unity3d.com>
date Mon, 20 Feb 2017 17:23:25 +0100
parents 3505a6be2988
children b4d1e85265c1
files kallithea/controllers/admin/repos.py kallithea/controllers/api/api.py kallithea/controllers/changelog.py kallithea/controllers/changeset.py kallithea/controllers/compare.py kallithea/controllers/feed.py kallithea/controllers/files.py kallithea/controllers/followers.py kallithea/controllers/forks.py kallithea/controllers/home.py kallithea/controllers/pullrequests.py kallithea/controllers/summary.py kallithea/lib/auth.py kallithea/lib/helpers.py kallithea/model/repo.py kallithea/model/scm.py kallithea/templates/base/base.html kallithea/templates/changelog/changelog_summary_data.html kallithea/templates/changeset/changeset_file_comment.html kallithea/templates/files/files_edit.html kallithea/templates/files/files_source.html kallithea/templates/files/files_ypjax.html kallithea/templates/pullrequests/pullrequest_show.html kallithea/templates/search/search_commit.html kallithea/templates/search/search_content.html kallithea/templates/search/search_path.html
diffstat 26 files changed, 129 insertions(+), 192 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/controllers/admin/repos.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/admin/repos.py	Mon Feb 20 17:23:25 2017 +0100
@@ -37,7 +37,7 @@
 from kallithea.config.routing import url
 from kallithea.lib import helpers as h
 from kallithea.lib.auth import LoginRequired, \
-    HasRepoPermissionAnyDecorator, NotAnonymous, HasPermissionAny
+    HasRepoPermissionLevelDecorator, NotAnonymous, HasPermissionAny
 from kallithea.lib.base import BaseRepoController, render, jsonify
 from kallithea.lib.utils import action_logger
 from kallithea.lib.vcs import RepositoryError
@@ -100,7 +100,7 @@
     def index(self, format='html'):
         _list = Repository.query(sorted=True).all()
 
-        c.repos_list = RepoList(_list, perm_set=['repository.admin'])
+        c.repos_list = RepoList(_list, perm_level='admin')
         repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
                                                    admin=True,
                                                    super_user_actions=True)
@@ -212,7 +212,7 @@
             return {'result': True}
         return {'result': False}
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def update(self, repo_name):
         c.repo_info = self._load_repo()
         self.__load_defaults(c.repo_info)
@@ -261,7 +261,7 @@
                     % repo_name, category='error')
         raise HTTPFound(location=url('edit_repo', repo_name=changed_name))
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def delete(self, repo_name):
         repo_model = RepoModel()
         repo = repo_model.get_by_repo_name(repo_name)
@@ -298,7 +298,7 @@
             raise HTTPFound(location=url('repos_group_home', group_name=repo.group.group_name))
         raise HTTPFound(location=url('repos'))
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit(self, repo_name):
         defaults = self.__load_data()
         c.repo_fields = RepositoryField.query() \
@@ -312,7 +312,7 @@
             encoding="UTF-8",
             force_defaults=False)
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_permissions(self, repo_name):
         c.repo_info = self._load_repo()
         repo_model = RepoModel()
@@ -363,7 +363,7 @@
                     category='error')
             raise HTTPInternalServerError()
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_fields(self, repo_name):
         c.repo_info = self._load_repo()
         c.repo_fields = RepositoryField.query() \
@@ -374,7 +374,7 @@
             raise HTTPFound(location=url('repo_edit_fields'))
         return render('admin/repos/repo_edit.html')
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def create_repo_field(self, repo_name):
         try:
             form_result = RepoFieldForm()().to_python(dict(request.POST))
@@ -395,7 +395,7 @@
             h.flash(msg, category='error')
         raise HTTPFound(location=url('edit_repo_fields', repo_name=repo_name))
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def delete_repo_field(self, repo_name, field_id):
         field = RepositoryField.get_or_404(field_id)
         try:
@@ -407,7 +407,7 @@
             h.flash(msg, category='error')
         raise HTTPFound(location=url('edit_repo_fields', repo_name=repo_name))
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_advanced(self, repo_name):
         c.repo_info = self._load_repo()
         c.default_user_id = User.get_default_user().user_id
@@ -416,7 +416,7 @@
             .filter(UserFollowing.follows_repository == c.repo_info).scalar()
 
         _repos = Repository.query(sorted=True).all()
-        read_access_repos = RepoList(_repos)
+        read_access_repos = RepoList(_repos, perm_level='read')
         c.repos_list = [(None, _('-- Not a fork --'))]
         c.repos_list += [(x.repo_id, x.repo_name)
                          for x in read_access_repos
@@ -435,7 +435,7 @@
             encoding="UTF-8",
             force_defaults=False)
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_advanced_journal(self, repo_name):
         """
         Sets this repository to be visible in public journal,
@@ -458,7 +458,7 @@
         raise HTTPFound(location=url('edit_repo_advanced', repo_name=repo_name))
 
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_advanced_fork(self, repo_name):
         """
         Mark given repository as a fork of another
@@ -483,7 +483,7 @@
 
         raise HTTPFound(location=url('edit_repo_advanced', repo_name=repo_name))
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_advanced_locking(self, repo_name):
         """
         Unlock repository when it is locked !
@@ -504,7 +504,7 @@
                     category='error')
         raise HTTPFound(location=url('edit_repo_advanced', repo_name=repo_name))
 
-    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
+    @HasRepoPermissionLevelDecorator('write')
     def toggle_locking(self, repo_name):
         try:
             repo = Repository.get_by_repo_name(repo_name)
@@ -523,7 +523,7 @@
                     category='error')
         raise HTTPFound(location=url('summary_home', repo_name=repo_name))
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_caches(self, repo_name):
         c.repo_info = self._load_repo()
         c.active = 'caches'
@@ -541,7 +541,7 @@
             raise HTTPFound(location=url('edit_repo_caches', repo_name=c.repo_name))
         return render('admin/repos/repo_edit.html')
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_remote(self, repo_name):
         c.repo_info = self._load_repo()
         c.active = 'remote'
@@ -556,7 +556,7 @@
             raise HTTPFound(location=url('edit_repo_remote', repo_name=c.repo_name))
         return render('admin/repos/repo_edit.html')
 
-    @HasRepoPermissionAnyDecorator('repository.admin')
+    @HasRepoPermissionLevelDecorator('admin')
     def edit_statistics(self, repo_name):
         c.repo_info = self._load_repo()
         repo = c.repo_info.scm_instance
--- a/kallithea/controllers/api/api.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/api/api.py	Mon Feb 20 17:23:25 2017 +0100
@@ -35,7 +35,7 @@
 from kallithea.controllers.api import JSONRPCController, JSONRPCError
 from kallithea.lib.auth import (
     PasswordGenerator, AuthUser, HasPermissionAnyDecorator,
-    HasPermissionAnyDecorator, HasPermissionAny, HasRepoPermissionAny,
+    HasPermissionAnyDecorator, HasPermissionAny, HasRepoPermissionLevel,
     HasRepoGroupPermissionAny, HasUserGroupPermissionAny)
 from kallithea.lib.utils import map_groups, repo2db_mapper
 from kallithea.lib.utils2 import (
@@ -277,10 +277,7 @@
         """
         repo = get_repo_or_error(repoid)
         if not HasPermissionAny('hg.admin')():
-            # check if we have admin permission for this repo !
-            if not HasRepoPermissionAny('repository.admin',
-                                        'repository.write')(
-                    repo_name=repo.repo_name):
+            if not HasRepoPermissionLevel('write')(repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
         try:
@@ -342,8 +339,7 @@
         repo = get_repo_or_error(repoid)
         if HasPermissionAny('hg.admin')():
             pass
-        elif HasRepoPermissionAny('repository.admin',
-                                  'repository.write')(repo_name=repo.repo_name):
+        elif HasRepoPermissionLevel('write')(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 != request.authuser.user_id:
@@ -1204,9 +1200,7 @@
         repo = get_repo_or_error(repoid)
 
         if not HasPermissionAny('hg.admin')():
-            # check if we have admin permission for this repo !
-            perms = ('repository.admin', 'repository.write', 'repository.read')
-            if not HasRepoPermissionAny(*perms)(repo_name=repo.repo_name):
+            if not HasRepoPermissionLevel('read')(repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
         members = []
@@ -1314,9 +1308,7 @@
         repo = get_repo_or_error(repoid)
 
         if not HasPermissionAny('hg.admin')():
-            # check if we have admin permission for this repo !
-            perms = ('repository.admin', 'repository.write', 'repository.read')
-            if not HasRepoPermissionAny(*perms)(repo_name=repo.repo_name):
+            if not HasRepoPermissionLevel('read')(repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
         ret_type = Optional.extract(ret_type)
@@ -1492,8 +1484,7 @@
         """
         repo = get_repo_or_error(repoid)
         if not HasPermissionAny('hg.admin')():
-            # check if we have admin permission for this repo !
-            if not HasRepoPermissionAny('repository.admin')(repo_name=repo.repo_name):
+            if not HasRepoPermissionLevel('admin')(repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
             if (name != repo.repo_name and
@@ -1590,9 +1581,7 @@
 
         if HasPermissionAny('hg.admin')():
             pass
-        elif HasRepoPermissionAny('repository.admin',
-                                  'repository.write',
-                                  'repository.read')(repo_name=repo.repo_name):
+        elif HasRepoPermissionLevel('read')(repo.repo_name):
             if not isinstance(owner, Optional):
                 # forbid setting owner for non-admins
                 raise JSONRPCError(
@@ -1669,8 +1658,7 @@
         repo = get_repo_or_error(repoid)
 
         if not HasPermissionAny('hg.admin')():
-            # check if we have admin permission for this repo !
-            if not HasRepoPermissionAny('repository.admin')(repo_name=repo.repo_name):
+            if not HasRepoPermissionLevel('admin')(repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
         try:
@@ -1821,10 +1809,7 @@
         perm = get_perm_or_error(perm)
         user_group = get_user_group_or_error(usergroupid)
         if not HasPermissionAny('hg.admin')():
-            # check if we have admin permission for this repo !
-            _perms = ('repository.admin',)
-            if not HasRepoPermissionAny(*_perms)(
-                    repo_name=repo.repo_name):
+            if not HasRepoPermissionLevel('admin')(repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
             # check if we have at least read permission for this user group !
@@ -1877,10 +1862,7 @@
         repo = get_repo_or_error(repoid)
         user_group = get_user_group_or_error(usergroupid)
         if not HasPermissionAny('hg.admin')():
-            # check if we have admin permission for this repo !
-            _perms = ('repository.admin',)
-            if not HasRepoPermissionAny(*_perms)(
-                    repo_name=repo.repo_name):
+            if not HasRepoPermissionLevel('admin')(repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
 
             # check if we have at least read permission for this user group !
--- a/kallithea/controllers/changelog.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/changelog.py	Mon Feb 20 17:23:25 2017 +0100
@@ -34,7 +34,7 @@
 
 import kallithea.lib.helpers as h
 from kallithea.config.routing import url
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
 from kallithea.lib.base import BaseRepoController, render
 from kallithea.lib.compat import json
 from kallithea.lib.graphmod import graph_data
@@ -92,8 +92,7 @@
         raise HTTPBadRequest()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def index(self, repo_name, revision=None, f_path=None):
         # Fix URL after page size form submission via GET
         # TODO: Somehow just don't send this extra junk in the GET URL
@@ -179,8 +178,7 @@
         return render('changelog/changelog.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def changelog_details(self, cs):
         if request.environ.get('HTTP_X_PARTIAL_XHR'):
             c.cs = c.db_repo_scm_instance.get_changeset(cs)
@@ -188,8 +186,7 @@
         raise HTTPNotFound()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def changelog_summary(self, repo_name):
         if request.environ.get('HTTP_X_PARTIAL_XHR'):
             _load_changelog_summary()
--- a/kallithea/controllers/changeset.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/changeset.py	Mon Feb 20 17:23:25 2017 +0100
@@ -38,7 +38,7 @@
 
 from kallithea.lib.compat import json
 import kallithea.lib.helpers as h
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator, \
     NotAnonymous
 from kallithea.lib.base import BaseRepoController, render, jsonify
 from kallithea.lib.utils import action_logger
@@ -337,33 +337,28 @@
                 return render('changeset/changeset_range.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def index(self, revision, method='show'):
         return self._index(revision, method=method)
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def changeset_raw(self, revision):
         return self._index(revision, method='raw')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def changeset_patch(self, revision):
         return self._index(revision, method='patch')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def changeset_download(self, revision):
         return self._index(revision, method='download')
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def comment(self, repo_name, revision):
         assert request.environ.get('HTTP_X_PARTIAL_XHR')
@@ -414,15 +409,14 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def delete_comment(self, repo_name, comment_id):
         co = ChangesetComment.get_or_404(comment_id)
         if co.repo.repo_name != repo_name:
             raise HTTPNotFound()
         owner = co.author_id == request.authuser.user_id
-        repo_admin = h.HasRepoPermissionAny('repository.admin')(repo_name)
+        repo_admin = h.HasRepoPermissionLevel('admin')(repo_name)
         if h.HasPermissionAny('hg.admin')() or repo_admin or owner:
             ChangesetCommentsModel().delete(comment=co)
             Session().commit()
@@ -431,8 +425,7 @@
             raise HTTPForbidden()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def changeset_info(self, repo_name, revision):
         if request.is_xhr:
@@ -444,8 +437,7 @@
             raise HTTPBadRequest()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def changeset_children(self, repo_name, revision):
         if request.is_xhr:
@@ -458,8 +450,7 @@
             raise HTTPBadRequest()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def changeset_parents(self, repo_name, revision):
         if request.is_xhr:
--- a/kallithea/controllers/compare.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/compare.py	Mon Feb 20 17:23:25 2017 +0100
@@ -39,7 +39,7 @@
 from kallithea.lib.vcs.utils.hgcompat import unionrepo
 from kallithea.lib import helpers as h
 from kallithea.lib.base import BaseRepoController, render
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
 from kallithea.lib import diffs
 from kallithea.model.db import Repository
 from kallithea.lib.diffs import LimitedDiffContainer
@@ -168,16 +168,14 @@
         return other_changesets, org_changesets, ancestors
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def index(self, repo_name):
         c.compare_home = True
         c.a_ref_name = c.cs_ref_name = _('Select changeset')
         return render('compare/compare_diff.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def compare(self, repo_name, org_ref_type, org_ref_name, other_ref_type, other_ref_name):
         org_ref_name = org_ref_name.strip()
         other_ref_name = other_ref_name.strip()
--- a/kallithea/controllers/feed.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/feed.py	Mon Feb 20 17:23:25 2017 +0100
@@ -36,7 +36,7 @@
 
 from kallithea import CONFIG
 from kallithea.lib import helpers as h
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
 from kallithea.lib.base import BaseRepoController
 from kallithea.lib.diffs import DiffProcessor, LimitedDiffContainer
 from kallithea.model.db import CacheInvalidation
@@ -52,8 +52,7 @@
 class FeedController(BaseRepoController):
 
     @LoginRequired(api_access=True)
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def __before__(self):
         super(FeedController, self).__before__()
 
--- a/kallithea/controllers/files.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/files.py	Mon Feb 20 17:23:25 2017 +0100
@@ -44,7 +44,7 @@
 from kallithea.lib.compat import OrderedDict
 from kallithea.lib.utils2 import convert_line_endings, detect_mode, safe_str, \
     str2bool, safe_int
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
 from kallithea.lib.base import BaseRepoController, render, jsonify
 from kallithea.lib.vcs.backends.base import EmptyChangeset
 from kallithea.lib.vcs.conf import settings
@@ -125,8 +125,7 @@
         return file_node
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def index(self, repo_name, revision, f_path, annotate=False):
         # redirect to given revision from form if given
         post_revision = request.POST.get('at_rev', None)
@@ -199,8 +198,7 @@
         return render('files/files.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def history(self, repo_name, revision, f_path):
         changeset = self.__get_cs(revision)
@@ -222,8 +220,7 @@
             return data
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def authors(self, repo_name, revision, f_path):
         changeset = self.__get_cs(revision)
         _file = changeset.get_node(f_path)
@@ -235,8 +232,7 @@
             return render('files/files_history_box.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def rawfile(self, repo_name, revision, f_path):
         cs = self.__get_cs(revision)
         file_node = self.__get_filenode(cs, f_path)
@@ -248,8 +244,7 @@
         return file_node.content
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def raw(self, repo_name, revision, f_path):
         cs = self.__get_cs(revision)
         file_node = self.__get_filenode(cs, f_path)
@@ -295,7 +290,7 @@
         return file_node.content
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
+    @HasRepoPermissionLevelDecorator('write')
     def delete(self, repo_name, revision, f_path):
         repo = c.db_repo
         if repo.enable_locking and repo.locked[0]:
@@ -355,7 +350,7 @@
         return render('files/files_delete.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
+    @HasRepoPermissionLevelDecorator('write')
     def edit(self, repo_name, revision, f_path):
         repo = c.db_repo
         if repo.enable_locking and repo.locked[0]:
@@ -421,7 +416,7 @@
         return render('files/files_edit.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
+    @HasRepoPermissionLevelDecorator('write')
     def add(self, repo_name, revision, f_path):
 
         repo = c.db_repo
@@ -502,8 +497,7 @@
         return render('files/files_add.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def archivefile(self, repo_name, fname):
         fileformat = None
         revision = None
@@ -589,8 +583,7 @@
         return get_chunked_archive(archive_path)
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def diff(self, repo_name, f_path):
         ignore_whitespace = request.GET.get('ignorews') == '1'
         line_context = safe_int(request.GET.get('context'), 3)
@@ -693,8 +686,7 @@
             return render('files/file_diff.html')
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def diff_2way(self, repo_name, f_path):
         diff1 = request.GET.get('diff1', '')
         diff2 = request.GET.get('diff2', '')
@@ -781,8 +773,7 @@
         return hist_l, changesets
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def nodelist(self, repo_name, revision, f_path):
         if request.environ.get('HTTP_X_PARTIAL_XHR'):
--- a/kallithea/controllers/followers.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/followers.py	Mon Feb 20 17:23:25 2017 +0100
@@ -29,7 +29,7 @@
 
 from pylons import tmpl_context as c, request
 
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
 from kallithea.lib.base import BaseRepoController, render
 from kallithea.lib.page import Page
 from kallithea.lib.utils2 import safe_int
@@ -44,8 +44,7 @@
         super(FollowersController, self).__before__()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def followers(self, repo_name):
         p = safe_int(request.GET.get('page'), 1)
         repo_id = c.db_repo.repo_id
--- a/kallithea/controllers/forks.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/forks.py	Mon Feb 20 17:23:25 2017 +0100
@@ -37,8 +37,8 @@
 import kallithea.lib.helpers as h
 
 from kallithea.config.routing import url
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
-    NotAnonymous, HasRepoPermissionAny, HasPermissionAnyDecorator, HasPermissionAny
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator, \
+    NotAnonymous, HasRepoPermissionLevel, HasPermissionAnyDecorator, HasPermissionAny
 from kallithea.lib.base import BaseRepoController, render
 from kallithea.lib.page import Page
 from kallithea.lib.utils2 import safe_int
@@ -108,16 +108,13 @@
         return defaults
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def forks(self, repo_name):
         p = safe_int(request.GET.get('page'), 1)
         repo_id = c.db_repo.repo_id
         d = []
         for r in Repository.get_repo_forks(repo_id):
-            if not HasRepoPermissionAny(
-                'repository.read', 'repository.write', 'repository.admin'
-            )(r.repo_name, 'get forks check'):
+            if not HasRepoPermissionLevel('read')(r.repo_name, 'get forks check'):
                 continue
             d.append(r)
         c.forks_pager = Page(d, page=p, items_per_page=20)
@@ -130,8 +127,7 @@
     @LoginRequired()
     @NotAnonymous()
     @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def fork(self, repo_name):
         c.repo_info = Repository.get_by_repo_name(repo_name)
         if not c.repo_info:
@@ -149,8 +145,7 @@
     @LoginRequired()
     @NotAnonymous()
     @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def fork_create(self, repo_name):
         self.__load_defaults()
         c.repo_info = Repository.get_by_repo_name(repo_name)
--- a/kallithea/controllers/home.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/home.py	Mon Feb 20 17:23:25 2017 +0100
@@ -35,7 +35,7 @@
 
 from kallithea.lib.utils import conditional_cache
 from kallithea.lib.compat import json
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
 from kallithea.lib.base import BaseController, render, jsonify
 from kallithea.model.db import Repository, RepoGroup
 from kallithea.model.repo import RepoModel
@@ -113,8 +113,7 @@
             raise HTTPBadRequest()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def repo_refs_data(self, repo_name):
         repo = Repository.get_by_repo_name(repo_name).scm_instance
--- a/kallithea/controllers/pullrequests.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/pullrequests.py	Mon Feb 20 17:23:25 2017 +0100
@@ -37,7 +37,7 @@
 from kallithea.config.routing import url
 from kallithea.lib import helpers as h
 from kallithea.lib import diffs
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator, \
     NotAnonymous
 from kallithea.lib.base import BaseRepoController, render, jsonify
 from kallithea.lib.compat import json, OrderedDict
@@ -190,8 +190,7 @@
         return request.authuser.admin or owner or reviewer
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def show_all(self, repo_name):
         c.from_ = request.GET.get('from_') or ''
         c.closed = request.GET.get('closed') or ''
@@ -236,8 +235,7 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def index(self):
         org_repo = c.db_repo
         org_scm_instance = org_repo.scm_instance
@@ -293,8 +291,7 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def repo_info(self, repo_name):
         repo = c.db_repo
@@ -307,8 +304,7 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def create(self, repo_name):
         repo = c.db_repo
         try:
@@ -513,8 +509,7 @@
     # pullrequest_post for PR editing
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def post(self, repo_name, pull_request_id):
         pull_request = PullRequest.get_or_404(pull_request_id)
         if pull_request.is_closed():
@@ -522,7 +517,7 @@
         assert pull_request.other_repo.repo_name == repo_name
         #only owner or admin can update it
         owner = pull_request.owner_id == request.authuser.user_id
-        repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
+        repo_admin = h.HasRepoPermissionLevel('admin')(c.repo_name)
         if not (h.HasPermissionAny('hg.admin')() or repo_admin or owner):
             raise HTTPForbidden()
 
@@ -571,8 +566,7 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def delete(self, repo_name, pull_request_id):
         pull_request = PullRequest.get_or_404(pull_request_id)
@@ -586,8 +580,7 @@
         raise HTTPForbidden()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def show(self, repo_name, pull_request_id, extra=None):
         repo_model = RepoModel()
         c.users_array = repo_model.get_users_js()
@@ -775,8 +768,7 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def comment(self, repo_name, pull_request_id):
         pull_request = PullRequest.get_or_404(pull_request_id)
@@ -800,8 +792,8 @@
         if delete == "delete":
             if (pull_request.owner_id == request.authuser.user_id or
                 h.HasPermissionAny('hg.admin')() or
-                h.HasRepoPermissionAny('repository.admin')(pull_request.org_repo.repo_name) or
-                h.HasRepoPermissionAny('repository.admin')(pull_request.other_repo.repo_name)
+                h.HasRepoPermissionLevel('admin')(pull_request.org_repo.repo_name) or
+                h.HasRepoPermissionLevel('admin')(pull_request.other_repo.repo_name)
                 ) and not pull_request.is_closed():
                 PullRequestModel().delete(pull_request)
                 Session().commit()
@@ -861,8 +853,7 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def delete_comment(self, repo_name, comment_id):
         co = ChangesetComment.get(comment_id)
@@ -871,7 +862,7 @@
             raise HTTPForbidden()
 
         owner = co.author_id == request.authuser.user_id
-        repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
+        repo_admin = h.HasRepoPermissionLevel('admin')(c.repo_name)
         if h.HasPermissionAny('hg.admin')() or repo_admin or owner:
             ChangesetCommentsModel().delete(comment=co)
             Session().commit()
--- a/kallithea/controllers/summary.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/controllers/summary.py	Mon Feb 20 17:23:25 2017 +0100
@@ -43,7 +43,7 @@
 from kallithea.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
 from kallithea.model.db import Statistics, CacheInvalidation, User
 from kallithea.lib.utils2 import safe_str
-from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
+from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator, \
     NotAnonymous
 from kallithea.lib.base import BaseRepoController, render, jsonify
 from kallithea.lib.vcs.backends.base import EmptyChangeset
@@ -107,8 +107,7 @@
         return _get_readme_from_cache(repo_name, kind)
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def index(self, repo_name):
         _load_changelog_summary()
 
@@ -161,8 +160,7 @@
 
     @LoginRequired()
     @NotAnonymous()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     @jsonify
     def repo_size(self, repo_name):
         if request.is_xhr:
@@ -171,8 +169,7 @@
             raise HTTPBadRequest()
 
     @LoginRequired()
-    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
-                                   'repository.admin')
+    @HasRepoPermissionLevelDecorator('read')
     def statistics(self, repo_name):
         if c.db_repo.enable_statistics:
             c.show_stats = True
--- a/kallithea/lib/auth.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/lib/auth.py	Mon Feb 20 17:23:25 2017 +0100
@@ -537,6 +537,18 @@
     def permissions(self):
         return self.__get_perms(user=self, cache=False)
 
+    def has_repository_permission_level(self, repo_name, level, purpose=None):
+        required_perms = {
+            'read': ['repository.read', 'repository.write', 'repository.admin'],
+            'write': ['repository.write', 'repository.admin'],
+            'admin': ['repository.admin'],
+        }[level]
+        actual_perm = self.permissions['repositories'].get(repo_name)
+        ok = actual_perm in required_perms
+        log.debug('Checking if user %r can %r repo %r (%s): %s (has %r)',
+            self.username, level, repo_name, purpose, ok, actual_perm)
+        return ok
+
     @property
     def api_keys(self):
         return self._get_api_keys()
@@ -836,17 +848,15 @@
         return any(p in global_permissions for p in self.required_perms)
 
 
-class HasRepoPermissionAnyDecorator(_PermsDecorator):
+class HasRepoPermissionLevelDecorator(_PermsDecorator):
     """
-    Checks the user has any of given permissions for the requested repository.
+    Checks the user has at least the specified permission level for the requested repository.
     """
 
     def check_permissions(self, user):
         repo_name = get_repo_slug(request)
-        try:
-            return user.permissions['repositories'][repo_name] in self.required_perms
-        except KeyError:
-            return False
+        (level,) = self.required_perms
+        return user.has_repository_permission_level(repo_name, level)
 
 
 class HasRepoGroupPermissionAnyDecorator(_PermsDecorator):
@@ -908,17 +918,11 @@
         return ok
 
 
-class HasRepoPermissionAny(_PermsFunction):
+class HasRepoPermissionLevel(_PermsFunction):
 
     def __call__(self, repo_name, purpose=None):
-        try:
-            ok = request.user.permissions['repositories'][repo_name] in self.required_perms
-        except KeyError:
-            ok = False
-
-        log.debug('Check %s for %s for repo %s (%s): %s' %
-            (request.user.username, self.required_perms, repo_name, purpose, ok))
-        return ok
+        (level,) = self.required_perms
+        return request.user.has_repository_permission_level(repo_name, level, purpose)
 
 
 class HasRepoGroupPermissionAny(_PermsFunction):
--- a/kallithea/lib/helpers.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/lib/helpers.py	Mon Feb 20 17:23:25 2017 +0100
@@ -778,7 +778,7 @@
 # PERMS
 #==============================================================================
 from kallithea.lib.auth import HasPermissionAny, \
-    HasRepoPermissionAny, HasRepoGroupPermissionAny
+    HasRepoPermissionLevel, HasRepoGroupPermissionAny
 
 
 #==============================================================================
--- a/kallithea/model/repo.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/model/repo.py	Mon Feb 20 17:23:25 2017 +0100
@@ -47,7 +47,7 @@
     Statistics, UserGroup, Ui, RepoGroup, RepositoryField
 
 from kallithea.lib import helpers as h
-from kallithea.lib.auth import HasRepoPermissionAny, HasUserGroupPermissionAny
+from kallithea.lib.auth import HasRepoPermissionLevel, HasUserGroupPermissionAny
 from kallithea.lib.exceptions import AttachedForksError
 from kallithea.model.scm import UserGroupList
 
@@ -207,10 +207,7 @@
         for repo in repos_list:
             if perm_check:
                 # check permission at this level
-                if not HasRepoPermissionAny(
-                        'repository.read', 'repository.write',
-                        'repository.admin'
-                )(repo.repo_name, 'get_repos_as_dict check'):
+                if not HasRepoPermissionLevel('read')(repo.repo_name, 'get_repos_as_dict check'):
                     continue
             cs_cache = repo.changeset_cache
             row = {
--- a/kallithea/model/scm.py	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/model/scm.py	Mon Feb 20 17:23:25 2017 +0100
@@ -49,7 +49,7 @@
 from kallithea.lib import helpers as h
 from kallithea.lib.utils2 import safe_str, safe_unicode, get_server_url, \
     _set_extras
-from kallithea.lib.auth import HasRepoPermissionAny, HasRepoGroupPermissionAny, \
+from kallithea.lib.auth import HasRepoPermissionLevel, HasRepoGroupPermissionAny, \
     HasUserGroupPermissionAny, HasPermissionAny, HasPermissionAny
 from kallithea.lib.utils import get_filesystem_repos, make_ui, \
     action_logger
@@ -114,13 +114,10 @@
 
 class RepoList(_PermCheckIterator):
 
-    def __init__(self, db_repo_list, perm_set=None, extra_kwargs=None):
-        if not perm_set:
-            perm_set = ['repository.read', 'repository.write', 'repository.admin']
-
+    def __init__(self, db_repo_list, perm_level, extra_kwargs=None):
         super(RepoList, self).__init__(obj_list=db_repo_list,
-                    obj_attr='repo_name', perm_set=perm_set,
-                    perm_checker=HasRepoPermissionAny,
+                    obj_attr='repo_name', perm_set=[perm_level],
+                    perm_checker=HasRepoPermissionLevel,
                     extra_kwargs=extra_kwargs)
 
 
@@ -216,7 +213,7 @@
 
     def get_repos(self, repos):
         """Return the repos the user has access to"""
-        return RepoList(repos)
+        return RepoList(repos, perm_level='read')
 
     def get_repo_groups(self, groups=None):
         """Return the repo groups the user has access to
--- a/kallithea/templates/base/base.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/base/base.html	Mon Feb 20 17:23:25 2017 +0100
@@ -133,13 +133,13 @@
           <input id="branch_switcher" name="branch_switcher" type="hidden">
         </li>
         <li class="${'active' if current == 'options' else ''} dropdown" data-context="options">
-             %if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
+             %if h.HasRepoPermissionLevel('admin')(c.repo_name):
                <a href="${h.url('edit_repo',repo_name=c.repo_name)}" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" aria-haspopup="true"><i class="icon-wrench"></i> ${_('Options')} <i class="caret"></i></a>
              %else:
                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" aria-haspopup="true"><i class="icon-wrench"></i> ${_('Options')} <i class="caret"></i></a>
              %endif
           <ul class="dropdown-menu" role="menu" aria-hidden="true">
-             %if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
+             %if h.HasRepoPermissionLevel('admin')(c.repo_name):
                    <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}"><i class="icon-gear"></i> ${_('Settings')}</a></li>
              %endif
               %if c.db_repo.fork:
@@ -150,7 +150,7 @@
 
               <li><a href="${h.url('search_repo',repo_name=c.repo_name)}"><i class="icon-search"></i> ${_('Search')}</a></li>
 
-              %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.db_repo.enable_locking:
+              %if h.HasRepoPermissionLevel('write')(c.repo_name) and c.db_repo.enable_locking:
                 %if c.db_repo.locked[0]:
                   <li><a href="${h.url('toggle_locking', repo_name=c.repo_name)}"><i class="icon-lock"></i> ${_('Unlock')}</a></li>
                 %else:
--- a/kallithea/templates/changelog/changelog_summary_data.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/changelog/changelog_summary_data.html	Mon Feb 20 17:23:25 2017 +0100
@@ -80,7 +80,7 @@
 </ul>
 %else:
 
-%if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
+%if h.HasRepoPermissionLevel('write')(c.repo_name):
 <h4>${_('Add or upload files directly via Kallithea')}</h4>
 <div style="margin: 20px 30px;">
   <div id="add_node_id" class="add_node">
--- a/kallithea/templates/changeset/changeset_file_comment.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/changeset/changeset_file_comment.html	Mon Feb 20 17:23:25 2017 +0100
@@ -24,7 +24,7 @@
               <a class="permalink" href="${co.url()}">&para;</a>
           </span>
 
-          %if co.author_id == request.authuser.user_id or h.HasRepoPermissionAny('repository.admin')(c.repo_name):
+          %if co.author_id == request.authuser.user_id or h.HasRepoPermissionLevel('admin')(c.repo_name):
             %if co.deletable():
               <div onClick="confirm('${_('Delete comment?')}') && deleteComment(${co.comment_id})" class="buttons delete-comment btn btn-default btn-xs" style="margin:0 5px">${_('Delete')}</div>
             %endif
@@ -80,7 +80,7 @@
                 %endfor
 
                 %if c.pull_request is not None and ( \
-                    h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) \
+                    h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionLevel('admin')(c.repo_name) \
                     or c.pull_request.owner_id == request.authuser.user_id):
                 <div>
                   ${_('Finish pull request')}:
--- a/kallithea/templates/files/files_edit.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/files/files_edit.html	Mon Feb 20 17:23:25 2017 +0100
@@ -48,7 +48,7 @@
                       ${h.link_to(_('Show Annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
                       ${h.link_to(_('Show as Raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
                       ${h.link_to(_('Download as Raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
-                      % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
+                      % if h.HasRepoPermissionLevel('write')(c.repo_name):
                        % if not c.file.is_binary:
                         ${h.link_to(_('Source'),h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
                        % endif
--- a/kallithea/templates/files/files_source.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/files/files_source.html	Mon Feb 20 17:23:25 2017 +0100
@@ -34,7 +34,7 @@
               %endif
               ${h.link_to(_('Show as Raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
               ${h.link_to(_('Download as Raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="btn btn-default btn-xs")}
-              %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
+              %if h.HasRepoPermissionLevel('write')(c.repo_name):
                %if c.on_branch_head and not c.file.is_binary:
                 ${h.link_to(_('Edit on Branch: %s') % c.changeset.branch, h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path, anchor='edit'),class_="btn btn-default btn-xs")}
                 ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path, anchor='edit'),class_="btn btn-danger btn-xs")}
--- a/kallithea/templates/files/files_ypjax.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/files/files_ypjax.html	Mon Feb 20 17:23:25 2017 +0100
@@ -5,7 +5,7 @@
         - ${_('annotation')}
         %endif
         %if c.file.is_dir():
-          % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
+          % if h.HasRepoPermissionLevel('write')(c.repo_name):
              / <span title="${_('Add New File')}">
                <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path, anchor='edit')}">
                    <i class="icon-plus-circled" style="color:#5bb75b; font-size: 16px"></i></a>
--- a/kallithea/templates/pullrequests/pullrequest_show.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/pullrequests/pullrequest_show.html	Mon Feb 20 17:23:25 2017 +0100
@@ -15,7 +15,7 @@
 </%block>
 
 <%def name="main()">
-<% editable = not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or c.pull_request.owner_id == request.authuser.user_id) %>
+<% editable = not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionLevel('admin')(c.repo_name) or c.pull_request.owner_id == request.authuser.user_id) %>
 ${self.repo_context_bar('showpullrequest')}
 <div class="panel panel-primary">
   <div class="panel-heading clearfix">
--- a/kallithea/templates/search/search_commit.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/search/search_commit.html	Mon Feb 20 17:23:25 2017 +0100
@@ -1,7 +1,7 @@
 ##commit highlighting
 
 %for cnt,sr in enumerate(c.formated_results):
-    %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
+    %if h.HasRepoPermissionLevel('read')(sr['repository'],'search results check'):
         <div id="body${cnt}" class="codeblock">
             <div class="code-header">
                 <div class="search-path">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['raw_id'])),
--- a/kallithea/templates/search/search_content.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/search/search_content.html	Mon Feb 20 17:23:25 2017 +0100
@@ -1,7 +1,7 @@
 ##content highlighting
 
 %for cnt,sr in enumerate(c.formated_results):
-    %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
+    %if h.HasRepoPermissionLevel('read')(sr['repository'],'search results check'):
         <div id="body${cnt}" class="codeblock">
             <div class="code-header">
                 <div class="search-path">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
--- a/kallithea/templates/search/search_path.html	Thu Feb 23 20:26:27 2017 +0100
+++ b/kallithea/templates/search/search_path.html	Mon Feb 20 17:23:25 2017 +0100
@@ -1,7 +1,7 @@
 ##path search
 
 %for cnt,sr in enumerate(c.formated_results):
-    %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
+    %if h.HasRepoPermissionLevel('read')(sr['repository'],'search results check'):
         <div class="panel panel-default">
             <div class="panel-heading">
                 ${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),