# HG changeset patch # User Sandu Turcan # Date 2018-02-18 23:27:53 # Node ID 133ba4b8705b10c0cc67f9a97cd6e5b35a11273b # Parent 940ad8b4d461c0263280de60aa33f1d80a3736a4 path-permissions: Path-based permissions for mercurial diff --git a/rhodecode/lib/vcs/backends/base.py b/rhodecode/lib/vcs/backends/base.py --- a/rhodecode/lib/vcs/backends/base.py +++ b/rhodecode/lib/vcs/backends/base.py @@ -24,9 +24,11 @@ Base module for all VCS systems import collections import datetime +import fnmatch import itertools import logging import os +import re import time import warnings @@ -1633,9 +1635,60 @@ class DiffChunk(object): class BasePathPermissionChecker(object): - def __init__(self, username, has_full_access = False): - self.username = username - self.has_full_access = has_full_access + @staticmethod + def create_from_patterns(includes, excludes): + if includes and '*' in includes and not excludes: + return AllPathPermissionChecker() + elif excludes and '*' in excludes: + return NonePathPermissionChecker() + else: + return PatternPathPermissionChecker(includes, excludes) + + @property + def has_full_access(self): + raise NotImplemented() def has_access(self, path): raise NotImplemented() + + +class AllPathPermissionChecker(BasePathPermissionChecker): + + @property + def has_full_access(self): + return True + + def has_access(self, path): + return True + + +class NonePathPermissionChecker(BasePathPermissionChecker): + + @property + def has_full_access(self): + return False + + def has_access(self, path): + return False + + +class PatternPathPermissionChecker(BasePathPermissionChecker): + + def __init__(self, includes, excludes): + self.includes = includes + self.excludes = excludes + self.includes_re = [] if not includes else [re.compile(fnmatch.translate(pattern)) for pattern in includes] + self.excludes_re = [] if not excludes else [re.compile(fnmatch.translate(pattern)) for pattern in excludes] + + @property + def has_full_access(self): + return '*' in self.includes and not self.excludes + + def has_access(self, path): + for re in self.excludes_re: + if re.match(path): + return False + for re in self.includes_re: + if re.match(path): + return True + return False \ No newline at end of file diff --git a/rhodecode/lib/vcs/backends/hg/repository.py b/rhodecode/lib/vcs/backends/hg/repository.py --- a/rhodecode/lib/vcs/backends/hg/repository.py +++ b/rhodecode/lib/vcs/backends/hg/repository.py @@ -21,7 +21,7 @@ """ HG repository module """ - +import ConfigParser import logging import binascii import os @@ -38,7 +38,7 @@ from rhodecode.lib.utils import safe_uni from rhodecode.lib.vcs import connection from rhodecode.lib.vcs.backends.base import ( BaseRepository, CollectionGenerator, Config, MergeResponse, - MergeFailureReason, Reference) + MergeFailureReason, Reference, BasePathPermissionChecker) from rhodecode.lib.vcs.backends.hg.commit import MercurialCommit from rhodecode.lib.vcs.backends.hg.diff import MercurialDiff from rhodecode.lib.vcs.backends.hg.inmemory import MercurialInMemoryCommit @@ -891,6 +891,33 @@ class MercurialRepository(BaseRepository self._remote.bookmark(bookmark, revision=revision) self._remote.invalidate_vcs_cache() + def get_path_permissions(self, username): + hgacl_file = self.path + '/.hg/hgacl' + if os.path.exists(hgacl_file): + hgacl = ConfigParser.RawConfigParser() + hgacl.read(hgacl_file) + def read_patterns(suffix): + svalue = None + try: + svalue = hgacl.get('narrowhgacl', username + suffix) + except ConfigParser.NoOptionError: + try: + svalue = hgacl.get('narrowhgacl', 'default' + suffix) + except ConfigParser.NoOptionError: + pass + if not svalue: + return None + result = ['/'] + for pattern in svalue.split(): + result.append(pattern) + if '*' not in pattern and '?' not in pattern: + result.append(pattern + '/*') + return result + includes = read_patterns('.includes') + excludes = read_patterns('.excludes') + return BasePathPermissionChecker.create_from_patterns(includes, excludes) + else: + return None class MercurialIndexBasedCollectionGenerator(CollectionGenerator):