Fix for issue #2486: Svnserve 1.3 authz: writing on subfolder requires read access on repository root
Removed overly restrictive tests for read access when opening root or opening a directory.
  
Patch by: Lieven Govaerts <lgo@mobsol.be>

* subversion/libsvn_repos/commit.c: 
  (open_root): removed check on read access
  (open_directory): removed check on read access
  
* subversion/tests/cmdline/authz_tests.py: 
  new file, contains tests for issue #2486.
  
* subversion/tests/cmdline/svntest/actions.py:
  (run_and_verify_commit): add --username and --password parameters to the 
   commit. 
   
* subversion/tests/cmdline/svntest/main.py:
  (get_authz_file_path): new function
  (get_svnserve_conf_file_path): new function
  
* subversion/tests/libsvn_repos/repos-test.c:
  (commit_editor_authz): removed a now obsolete part of the test.

Index: subversion/libsvn_repos/commit.c
===================================================================
--- subversion/libsvn_repos/commit.c    (revision 18663)
+++ subversion/libsvn_repos/commit.c    (working copy)
@@ -208,9 +208,6 @@
   SVN_ERR(svn_fs_txn_name(&(eb->txn_name), eb->txn, eb->pool));
   SVN_ERR(svn_fs_txn_root(&(eb->txn_root), eb->txn, eb->pool));
 
-  /* Check read access to root */
-  SVN_ERR(check_authz(eb, "/", eb->txn_root, svn_authz_read, pool));
-
   /* Create a root dir baton.  The `base_path' field is an -absolute-
      path in the filesystem, upon which all further editor paths are
      based. */
@@ -388,10 +385,6 @@
   svn_node_kind_t kind;
   const char *full_path = svn_path_join(eb->base_path, path, pool);
 
-  /* Check for read authorization. */
-  SVN_ERR(check_authz(eb, full_path, eb->txn_root,
-                      svn_authz_read, pool));
-
   /* Check PATH in our transaction.  If it does not exist,
      return a 'Path not present' error. */
   SVN_ERR(svn_fs_check_path(&kind, eb->txn_root, full_path, pool));
Index: subversion/tests/cmdline/authz_tests.py
===================================================================
--- subversion/tests/cmdline/authz_tests.py (revision 0)
+++ subversion/tests/cmdline/authz_tests.py (revision 0)
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+#
+#  authz_tests.py:  testing authentication.
+#
+#  Subversion is a tool for revision control. 
+#  See http://subversion.tigris.org for more information.
+#    
+# ====================================================================
+# Copyright (c) 2000-2006 CollabNet.  All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution.  The terms
+# are also available at http://subversion.tigris.org/license-1.html.
+# If newer versions of this license are posted there, you may use a
+# newer version instead, at your option.
+#
+######################################################################
+
+# General modules
+import os
+
+# Our testing module
+import svntest
+
+
+# (abbreviation)
+Item = svntest.wc.StateItem
+
+######################################################################
+# Utilities
+#
+
+def write_restrictive_svnserve_conf(repo_dir):
+  "Create a restrictive authz file ( no anynomous access )."
+  
+  fp = open(svntest.main.get_svnserve_conf_file_path(repo_dir), 'w')
+  fp.write("[general]\nanon-access = none\nauth-access = write\n"
+           "password-db = passwd\nauthz-db = authz\n")
+  fp.close()
+
+def skip_test_when_no_authz_available():
+  "skip this test on local repository"
+  if svntest.main.test_area_url.startswith('file://'):
+    raise svntest.Skip
+    
+######################################################################
+# Tests
+#
+#   Each test must return on success or raise on failure.
+
+
+#----------------------------------------------------------------------
+
+# regression test for issue #2486 - part 1: open_root
+
+def authz_open_root(sbox):
+  "authz issue #2486 - open root"
+  sbox.build()
+  
+  skip_test_when_no_authz_available()
+  
+  fp = open(svntest.main.get_authz_file_path(
+              svntest.main.current_repo_dir), 'w')
+  fp.write("[/]\n\n[/A]\njrandom = rw\n")
+  fp.close()
+  
+  write_restrictive_svnserve_conf(svntest.main.current_repo_dir)
+
+  # we have write access in folder /A, but not in root. Test on too
+  # restrictive access needed in open_root by modifying a file in /A
+  wc_dir = sbox.wc_dir
+  
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+  svntest.main.file_append(mu_path, "hi")
+  
+  # Create expected output tree.
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/mu' : Item(verb='Sending'),
+    })
+
+  # Commit the one file.
+  svntest.actions.run_and_verify_commit (wc_dir,
+                                         expected_output,
+                                         None,
+                                         None,
+                                         None, None,
+                                         None, None,
+                                         mu_path)
+
+#----------------------------------------------------------------------
+
+# regression test for issue #2486 - part 2: open_directory
+
+def authz_open_directory(sbox):
+  "authz issue #2486 - open directory"
+  sbox.build()
+  
+  skip_test_when_no_authz_available()
+  
+  fp = open(svntest.main.get_authz_file_path(
+              svntest.main.current_repo_dir), 'w')
+  fp.write("[/]\n*=rw\n[/A/B]\n*=\n[/A/B/E]\njrandom = rw\n")
+  fp.close()
+  
+  write_restrictive_svnserve_conf(svntest.main.current_repo_dir) 
+
+  # we have write access in folder /A/B/E, but not in /A/B. Test on too
+  # restrictive access needed in open_directory by moving file /A/mu to
+  # /A/B/E
+  wc_dir = sbox.wc_dir
+  
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+  E_path = os.path.join(wc_dir, 'A', 'B', 'E')
+  
+  svntest.main.run_svn(None, 'mv', mu_path, E_path)
+  
+  # Create expected output tree.
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/mu' : Item(verb='Deleting'),
+    'A/B/E/mu' : Item(verb='Adding'),
+    })
+  
+  # Commit the working copy.
+  svntest.actions.run_and_verify_commit (wc_dir,
+                                         expected_output,
+                                         None,
+                                         None,
+                                         None, None,
+                                         None, None,
+                                         wc_dir)
+
+########################################################################
+# Run the tests
+
+
+# list all tests here, starting with None:
+test_list = [ None,
+              authz_open_root,
+              authz_open_directory,
+             ]
+
+if __name__ == '__main__':
+  svntest.main.run_tests(test_list)
+  # NOTREACHED
+
+
+### End of file.
Index: subversion/tests/cmdline/svntest/actions.py
===================================================================
--- subversion/tests/cmdline/svntest/actions.py (revision 18663)
+++ subversion/tests/cmdline/svntest/actions.py (working copy)
@@ -568,7 +568,11 @@
     status_output_tree = status_output_tree.old_tree()
 
   # Commit.
-  output, errput = main.run_svn(error_re_string, 'ci', '-m', 'log msg', *args)
+  output, errput = main.run_svn(error_re_string, 'ci', 
+                                '--username', main.wc_author,
+                                '--password', main.wc_passwd, 
+                                '-m', 'log msg', 
+                                *args)
 
   if (error_re_string):
     rm = re.compile(error_re_string)
Index: subversion/tests/cmdline/svntest/main.py
===================================================================
--- subversion/tests/cmdline/svntest/main.py    (revision 18663)
+++ subversion/tests/cmdline/svntest/main.py    (working copy)
@@ -224,7 +224,16 @@
 
   return os.path.join(repo_dir, "hooks", "pre-revprop-change")
 
+def get_authz_file_path(repo_dir):
+  "Return the path of the authz file in REPO_DIR."
 
+  return os.path.join(repo_dir, "conf", "authz")
+
+def get_svnserve_conf_file_path(repo_dir):
+  "Return the path of the svnserve.conf file in REPO_DIR."
+
+  return os.path.join(repo_dir, "conf", "svnserve.conf")
+
 # Run any binary, logging the command line (TODO: and return code)
 def run_command(command, error_expected, binary_mode=0, *varargs):
   """Run COMMAND with VARARGS; return stdout, stderr as lists of lines.
Index: subversion/tests/libsvn_repos/repos-test.c
===================================================================
--- subversion/tests/libsvn_repos/repos-test.c  (revision 18663)
+++ subversion/tests/libsvn_repos/repos-test.c  (working copy)
@@ -1526,16 +1526,6 @@
                                 youngest_rev, subpool,
                                 &dir2_baton));
 
-  /* Test denied access to a directory. */
-  err = editor->open_directory("/A/C", dir_baton, SVN_INVALID_REVNUM,
-                               subpool, &dir2_baton);
-  if (err == SVN_NO_ERROR || err->apr_err != SVN_ERR_AUTHZ_UNREADABLE)
-    return svn_error_createf(SVN_ERR_TEST_FAILED, err,
-                             "Got %s error instead of expected "
-                             "SVN_ERR_AUTHZ_UNREADABLE",
-                             err ? "unexpected" : "no");
-  svn_error_clear(err);
-
   /* Open /A/D.  This should be granted. */
   SVN_ERR(editor->open_directory("/A/D", dir_baton, SVN_INVALID_REVNUM,
                                  subpool, &dir_baton));


