Edgewall Software

Ticket #9511: 9511-repository-dir-prefix-r10018.patch

File 9511-repository-dir-prefix-r10018.patch, 7.1 KB (added by rblank, 21 months ago)

Allow restricting the paths to repositories in the repository admin panel.

  • trac/util/__init__.py

    diff --git a/trac/util/__init__.py b/trac/util/__init__.py
    a b  
    341341    copytree_rec(str_path(src), str_path(dst)) 
    342342 
    343343 
     344def is_path_below(path, parent): 
     345    """Return True iff `path` is equal to parent or is located below `parent` 
     346    at any level. 
     347    """ 
     348    path = os.path.abspath(path) 
     349    parent = os.path.abspath(parent) 
     350    return path == parent or path.startswith(parent + os.sep) 
     351 
     352 
    344353# -- sys utils 
    345354 
    346355def arity(f): 
  • trac/util/tests/__init__.py

    diff --git a/trac/util/tests/__init__.py b/trac/util/tests/__init__.py
    a b  
    8080        self.assertEqual('test content', util.read_file(self.path)) 
    8181 
    8282 
     83class PathTestCase(unittest.TestCase): 
     84     
     85    def assert_below(self, path, parent): 
     86        self.assert_(util.is_path_below(path.replace('/', os.sep), 
     87                                        parent.replace('/', os.sep))) 
     88 
     89    def assert_not_below(self, path, parent): 
     90        self.assert_(not util.is_path_below(path.replace('/', os.sep), 
     91                                            parent.replace('/', os.sep))) 
     92 
     93    def test_is_path_below(self): 
     94        self.assert_below('/svn/project1', '/svn/project1') 
     95        self.assert_below('/svn/project1/repos', '/svn/project1') 
     96        self.assert_below('/svn/project1/sub/repos', '/svn/project1') 
     97        self.assert_below('/svn/project1/sub/../repos', '/svn/project1') 
     98        self.assert_not_below('/svn/project2/repos', '/svn/project1') 
     99        self.assert_not_below('/svn/project2/sub/repos', '/svn/project1') 
     100        self.assert_not_below('/svn/project1/../project2/repos', 
     101                              '/svn/project1') 
     102        self.assert_(util.is_path_below('repos', os.path.join(os.getcwd()))) 
     103        self.assert_(not util.is_path_below('../sub/repos', 
     104                                            os.path.join(os.getcwd()))) 
     105 
     106 
    83107class ContentDispositionTestCase(unittest.TestCase): 
    84108 
    85109    def test_filename(self): 
     
    96120def suite(): 
    97121    suite = unittest.TestSuite() 
    98122    suite.addTest(unittest.makeSuite(AtomicFileTestCase, 'test')) 
     123    suite.addTest(unittest.makeSuite(PathTestCase, 'test')) 
    99124    suite.addTest(unittest.makeSuite(ContentDispositionTestCase, 'test')) 
    100125    suite.addTest(concurrency.suite()) 
    101126    suite.addTest(datefmt.suite()) 
  • trac/versioncontrol/admin.py

    diff --git a/trac/versioncontrol/admin.py b/trac/versioncontrol/admin.py
    a b  
    1111# individuals. For the exact contribution history, see the revision 
    1212# history and logs, available at http://trac.edgewall.org/. 
    1313 
     14import os.path 
    1415import sys 
    1516 
    1617from genshi.builder import tag 
    1718 
    1819from trac.admin import IAdminCommandProvider, IAdminPanelProvider 
    19 from trac.config import _TRUE_VALUES 
     20from trac.config import _TRUE_VALUES, ListOption 
    2021from trac.core import * 
    2122from trac.perm import IPermissionRequestor 
     23from trac.util import is_path_below 
    2224from trac.util.text import breakable_path, normalize_whitespace, print_table, \ 
    2325                           printout 
    2426from trac.util.translation import _, ngettext, tag_ 
     
    164166 
    165167    implements(IAdminPanelProvider) 
    166168 
     169    repository_dir_prefixes = ListOption('trac', 'repository_dir_prefixes', '', 
     170        doc="""Comma-separated list of allowed prefixes for repository 
     171        directories when adding and editing repositories in the repository 
     172        admin panel. If the list is empty, all repository directories are 
     173        allowed. (''since 0.12.1'')""") 
     174 
    167175    # IAdminPanelProvider methods 
    168176 
    169177    def get_admin_panels(self, req): 
     
    198206                        if (value is not None or field == 'hidden') \ 
    199207                                and value != info.get(field): 
    200208                            changes[field] = value 
     209                    if 'dir' in changes \ 
     210                            and not self._check_dir(req, changes['dir']): 
     211                        changes = {} 
    201212                    if changes: 
    202213                        db_provider.modify_repository(reponame, changes) 
    203214                        add_notice(req, _('Your changes have been saved.')) 
     
    222233                                   'hook to call %(cset_added)s with the new ' 
    223234                                   'repository name.', cset_added=cset_added) 
    224235                        add_notice(req, msg) 
    225                     req.redirect(req.href.admin(category, page)) 
     236                    if changes: 
     237                        req.redirect(req.href.admin(category, page)) 
    226238             
    227239            Chrome(self.env).add_wiki_toolbars(req) 
    228240            data = {'view': 'detail', 'reponame': reponame} 
     
    234246                if db_provider and req.args.get('add_repos'): 
    235247                    name = req.args.get('name') 
    236248                    type_ = req.args.get('type') 
    237                     dir = req.args.get('dir') 
    238                     if name is not None and type_ is not None and dir: 
    239                         # Avoid errors when copy/pasting paths 
    240                         dir = normalize_whitespace(dir) 
     249                    # Avoid errors when copy/pasting paths 
     250                    dir = normalize_whitespace(req.args.get('dir', '')) 
     251                    prefixes = [os.path.join(self.env.path, prefix) 
     252                                for prefix in self.repository_dir_prefixes] 
     253                    if name is None or type_ is None or not dir: 
     254                        add_warning(req, _('Missing arguments to add a ' 
     255                                           'repository.')) 
     256                    elif self._check_dir(req, dir): 
    241257                        db_provider.add_repository(name, dir, type_) 
    242258                        name = name or '(default)' 
    243259                        add_notice(req, _('The repository "%(name)s" has been ' 
     
    256272                                   cset_added=cset_added) 
    257273                        add_notice(req, msg) 
    258274                        req.redirect(req.href.admin(category, page)) 
    259                     add_warning(req, _('Missing arguments to add a ' 
    260                                        'repository.')) 
    261275                 
    262276                # Add a repository alias 
    263277                elif db_provider and req.args.get('add_alias'): 
     
    319333            except Exception: 
    320334                pass 
    321335        return info 
     336 
     337    def _check_dir(self, req, dir): 
     338        """Check that a repository directory is valid, and add a warning 
     339        message if not. 
     340        """ 
     341        if not os.path.isabs(dir): 
     342            add_warning(req, _('The repository directory must be an absolute ' 
     343                               'path.')) 
     344            return False 
     345        prefixes = [os.path.join(self.env.path, prefix) 
     346                    for prefix in self.repository_dir_prefixes] 
     347        if prefixes and not any(is_path_below(dir, prefix) 
     348                                for prefix in prefixes): 
     349            add_warning(req, _('The repository directory must be located ' 
     350                               'below one of the following directories: ' 
     351                               '%(dirs)s', dirs=', '.join(prefixes))) 
     352            return False 
     353        return True