Index: subversion/mod_authz_svn/mod_authz_svn.c
===================================================================
--- subversion/mod_authz_svn/mod_authz_svn.c	(révision 15231)
+++ subversion/mod_authz_svn/mod_authz_svn.c	(copie de travail)
@@ -3,7 +3,7 @@
  *                  based authorization for a Subversion repository.
  *
  * ====================================================================
- * Copyright (c) 2003-2004 CollabNet.  All rights reserved.
+ * Copyright (c) 2003-2005 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
@@ -33,17 +33,11 @@
 #include "svn_path.h"
 #include "svn_config.h"
 #include "svn_string.h"
+#include "svn_repos.h"
 
 
 extern module AP_MODULE_DECLARE_DATA authz_svn_module;
 
-enum {
-    AUTHZ_SVN_NONE = 0,
-    AUTHZ_SVN_READ = 1,
-    AUTHZ_SVN_WRITE = 2,
-    AUTHZ_SVN_RECURSIVE = 4
-};
-
 typedef struct {
     int authoritative;
     int anonymous;
@@ -51,20 +45,6 @@
     const char *access_file;
 } authz_svn_config_rec;
 
-struct parse_authz_baton {
-    apr_pool_t *pool;
-    svn_config_t *config;
-    const char *user;
-    int allow;
-    int deny;
-
-    int required_access;
-    const char *repos_path;
-    const char *qualified_repos_path;
-
-    int access;
-};
-
 /*
  * Configuration
  */
@@ -100,223 +80,6 @@
     { NULL }
 };
 
-
-/*
- * Access checking
- */
-
-static int group_contains_user_internal(svn_config_t *cfg,
-    const char *group, const char *user, apr_hash_t *checked_groups,
-    apr_pool_t *pool)
-{
-    const char *value;
-    apr_array_header_t *list;
-    int i;
-
-    svn_config_get(cfg, &value, "groups", group, "");
-    list = svn_cstring_split(value, ",", TRUE, pool);
-
-    for (i = 0; i < list->nelts; i++) {
-       const char *group_user = APR_ARRAY_IDX(list, i, char *);
-
-       if (*group_user == '@') {
-           /* Guard against circular dependencies by checking group
-            * name against hash.
-            */
-           if (apr_hash_get(checked_groups, &group_user[1],
-                            APR_HASH_KEY_STRING))
-               continue;
-	   
-           /* Add group to hash of checked groups. */
-           apr_hash_set(checked_groups, &group_user[1],
-                        APR_HASH_KEY_STRING, "");
-
-           if (group_contains_user_internal(cfg, &group_user[1], user,
-                                            checked_groups, pool))
-               return 1;
-
-       } else if (!strcmp(user, group_user)) {
-           return 1;
-       }
-    }
-
-    return 0;
-}
-
-static int group_contains_user(svn_config_t *cfg,
-    const char *group, const char *user, apr_pool_t *pool)
-{
-    return group_contains_user_internal(cfg, group, user,
-                                        apr_hash_make(pool), pool);
-}
-
-static svn_boolean_t parse_authz_line(const char *name, const char *value,
-                                      void *baton)
-{
-    struct parse_authz_baton *b = baton;
-
-    if (strcmp(name, "*")) {
-        if (!b->user) {
-            return TRUE;
-        }
-
-        if (*name == '@') {
-            if (!group_contains_user(b->config, &name[1], b->user, b->pool))
-                return TRUE;
-        }
-        else if (strcmp(name, b->user)) {
-            return TRUE;
-        }
-    }
-
-    if (ap_strchr_c(value, 'r')) {
-        b->allow |= AUTHZ_SVN_READ;
-    }
-    else {
-        b->deny |= AUTHZ_SVN_READ;
-    }
-
-    if (ap_strchr_c(value, 'w')) {
-        b->allow |= AUTHZ_SVN_WRITE;
-    }
-    else {
-        b->deny |= AUTHZ_SVN_WRITE;
-    }
-
-    ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, b->pool,
-                  "%s = %s => allow = %i, deny = %i",
-                  name, value, b->allow, b->deny);
-
-    return TRUE;
-}
-
-/*
- * Return TRUE when ACCESS has been determined.
- */
-static int parse_authz_lines(svn_config_t *cfg,
-                             const char *repos_name, const char *repos_path,
-                             const char *user,
-                             int required_access, int *granted_access,
-                             apr_pool_t *pool)
-{
-    const char *qualified_repos_path;
-    struct parse_authz_baton baton = { 0 };
-
-    baton.pool = pool;
-    baton.config = cfg;
-    baton.user = user;
-
-    /* First try repos specific */
-    qualified_repos_path = apr_pstrcat(pool, repos_name, ":", repos_path,
-                                       NULL);
-    svn_config_enumerate(cfg, qualified_repos_path,
-                         parse_authz_line, &baton);
-    *granted_access = !(baton.deny & required_access)
-                      || (baton.allow & required_access);
-
-    if ((baton.deny & required_access)
-        || (baton.allow & required_access))
-        return TRUE;
-
-    svn_config_enumerate(cfg, repos_path,
-                         parse_authz_line, &baton);
-    *granted_access = !(baton.deny & required_access)
-                      || (baton.allow & required_access);
-
-    return (baton.deny & required_access)
-           || (baton.allow & required_access);
-}
-
-static svn_boolean_t parse_authz_section(const char *section_name,
-                                         void *baton)
-{
-  struct parse_authz_baton *b = baton;
-  int conclusive;
-
-  if (strncmp(section_name, b->qualified_repos_path,
-              strlen(b->qualified_repos_path))
-      && strncmp(section_name, b->repos_path,
-                 strlen(b->repos_path))) {
-      /* No match, move on to the next section. */
-      return TRUE;
-  }
-
-  b->allow = b->deny = 0;
-  svn_config_enumerate(b->config, section_name,
-                       parse_authz_line, b);
-
-  conclusive = (b->deny & b->required_access)
-               || (b->allow & b->required_access);
-
-  b->access = !(b->deny & b->required_access)
-              || (b->allow & b->required_access)
-              || !conclusive;
-  
-  /* If access isn't denied, move on to check the next section. */
-  return b->access;
-}
-
-static int parse_authz_sections(svn_config_t *cfg,
-                                const char *repos_name, const char *repos_path,
-                                const char *user,
-                                int required_access,
-                                apr_pool_t *pool)
-{
-    struct parse_authz_baton baton = { 0 };
-
-    baton.pool = pool;
-    baton.config = cfg;
-    baton.user = user;
-    baton.required_access = required_access;
-    baton.repos_path = repos_path;
-    baton.qualified_repos_path = apr_pstrcat(pool, repos_name, ":",
-                                             repos_path, NULL);
-    
-    baton.access = 1; /* Allow by default */
-    svn_config_enumerate_sections(cfg, parse_authz_section, &baton);
-
-    return baton.access;
-}
-
-static int check_access(svn_config_t *cfg, const char *repos_name,
-                        const char *repos_path, const char *user,
-                        int required_access, apr_pool_t *pool)
-{
-    const char *base_name;
-    const char *original_repos_path = repos_path;
-    int granted_access;
-
-    if (!repos_path) {
-        /* XXX: Check if the user has 'required_access' _anywhere_ in the
-         * XXX: repository.  For now, make this always succeed, until
-         * XXX: we come up with a good way of figuring this out.
-         */
-        return 1;
-    }
-
-    base_name = repos_path;
-    while (!parse_authz_lines(cfg, repos_name, repos_path,
-                              user, required_access, &granted_access,
-                              pool)) {
-        if (base_name[0] == '/' && base_name[1] == '\0') {
-            /* By default, deny access */
-            return 0;
-        }
-
-        svn_path_split(repos_path, &repos_path, &base_name, pool);
-    }
-
-    if (granted_access && (required_access & AUTHZ_SVN_RECURSIVE) != 0) {
-        /* Check access on entries below the current repos path */
-        granted_access = parse_authz_sections(cfg,
-                                              repos_name, original_repos_path,
-                                              user, required_access,
-                                              pool);
-    }
-
-    return granted_access;
-}
-
 /* Check if the current request R is allowed.  Upon exit *REPOS_PATH_REF
  * will contain the path and repository name that an operation was requested
  * on in the form 'name:path'.  *DEST_REPOS_PATH_REF will contain the
@@ -339,7 +102,8 @@
     const char *repos_path;
     const char *dest_repos_path = NULL;
     dav_error *dav_err;
-    int authz_svn_type = 0;
+    svn_repos_authz_access_t authz_svn_type = svn_authz_none;
+    svn_boolean_t authz_access_granted = FALSE;
     svn_config_t *access_conf = NULL;
     svn_error_t *svn_err;
     const char *cache_key;
@@ -348,20 +112,20 @@
     switch (r->method_number) {
     /* All methods requiring read access to all subtrees of r->uri */
     case M_COPY:
-        authz_svn_type |= AUTHZ_SVN_RECURSIVE;
+      authz_svn_type |= svn_authz_recursive;
 
     /* All methods requiring read access to r->uri */
     case M_OPTIONS:
     case M_GET:
     case M_PROPFIND:
     case M_REPORT:
-        authz_svn_type |= AUTHZ_SVN_READ;
-        break;
+      authz_svn_type |= svn_authz_read;
+      break;
 
     /* All methods requiring write access to all subtrees of r->uri */
     case M_MOVE:
     case M_DELETE:
-        authz_svn_type |= AUTHZ_SVN_RECURSIVE;
+      authz_svn_type |= svn_authz_recursive;
 
     /* All methods requiring write access to r->uri */
     case M_MKCOL:
@@ -372,13 +136,13 @@
     case M_MKACTIVITY:
     case M_LOCK:
     case M_UNLOCK:
-        authz_svn_type |= AUTHZ_SVN_WRITE;
-        break;
+      authz_svn_type |= svn_authz_write;
+      break;
 
     default:
-        /* Require most strict access for unknown methods */
-        authz_svn_type |= AUTHZ_SVN_WRITE|AUTHZ_SVN_RECURSIVE;
-        break;
+      /* Require most strict access for unknown methods */
+      authz_svn_type |= svn_authz_write | svn_authz_recursive;
+      break;
     }
 
     dav_err = dav_svn_split_uri(r,
@@ -486,12 +250,29 @@
                               NULL, r->connection->pool);
     }
 
-    if (!check_access(access_conf,
-                      repos_name, repos_path,
-                      r->user, authz_svn_type,
-                      r->pool)) {
-        return DECLINED;
+    /* Perform authz access control */
+    svn_err = svn_repos_authz_check_access(access_conf, repos_name,
+                                           repos_path, r->user,
+                                           authz_svn_type,
+                                           &authz_access_granted,
+                                           r->pool);
+    if (svn_err) {
+      ap_log_rerror(APLOG_MARK, APLOG_ERR,
+                    /* If it is an error code that APR can make sense
+                       of, then show it, otherwise, pass zero to avoid
+                       putting "APR does not understand this error code"
+                       in the error log. */
+                    ((svn_err->apr_err >= APR_OS_START_USERERR &&
+                      svn_err->apr_err < APR_OS_START_CANONERR) ?
+                     0 : svn_err->apr_err),
+                    r, "Failed to perform access control: %s",
+                    svn_err->message);
+      svn_error_clear(svn_err);
+
+      return DECLINED;
     }
+    if (!authz_access_granted)
+      return DECLINED;
 
     /* XXX: MKCOL, MOVE, DELETE
      * XXX: Require write access to the parent dir of repos_path.
@@ -508,13 +289,32 @@
         return OK;
     }
 
-    /* Check access on the first repos_path */
-    if (!check_access(access_conf,
-                      dest_repos_name, dest_repos_path,
-                      r->user, AUTHZ_SVN_WRITE|AUTHZ_SVN_RECURSIVE,
-                      r->pool)) {
-        return DECLINED;
+    /* Check access on the destination repos_path */
+    svn_err = svn_repos_authz_check_access(access_conf,
+                                           dest_repos_name,
+                                           dest_repos_path,
+                                           r->user,
+                                           svn_authz_write
+                                           |svn_authz_recursive,
+                                           &authz_access_granted,
+                                           r->pool);
+    if (svn_err) {
+      ap_log_rerror(APLOG_MARK, APLOG_ERR,
+                    /* If it is an error code that APR can make sense
+                       of, then show it, otherwise, pass zero to avoid
+                       putting "APR does not understand this error code"
+                       in the error log. */
+                    ((svn_err->apr_err >= APR_OS_START_USERERR &&
+                      svn_err->apr_err < APR_OS_START_CANONERR) ?
+                     0 : svn_err->apr_err),
+                    r, "Failed to perform access control: %s",
+                    svn_err->message);
+      svn_error_clear(svn_err);
+
+      return DECLINED;
     }
+    if (!authz_access_granted)
+      return DECLINED;
 
     /* XXX: MOVE and COPY, if the path doesn't exist yet, also
      * XXX: require write access to the parent dir of dest_repos_path.
@@ -577,7 +377,6 @@
                     "Access denied: - %s %s",
                     r->method, repos_path);
             }
-
         }
 
         return HTTP_FORBIDDEN;
Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h	(révision 15231)
+++ subversion/include/svn_repos.h	(copie de travail)
@@ -30,8 +30,8 @@
 #include "svn_types.h"
 #include "svn_error.h"
 #include "svn_version.h"
+#include "svn_config.h"
 
-
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
@@ -1672,6 +1672,35 @@
 
 /** @} */
 
+/** An enum defining the kinds of access authz looks up */
+typedef enum
+  {
+    /** no access */
+    svn_authz_none = 0,
+
+    /** Path can be read */
+    svn_authz_read = 1,
+
+    /** Path can be altered */
+    svn_authz_write = 2,
+
+    /** The other access credentials are recursive */
+    svn_authz_recursive = 4
+  } svn_repos_authz_access_t;
+
+/**
+ * Check wether @a user can access @a path in the repository @a
+ * repos_name with the @a required_access. @a cfg lists the ACLs to
+ * check against. Set @a *access_granted to indicate if the requested
+ * access is granted.
+ */
+svn_error_t *
+svn_repos_authz_check_access (svn_config_t *cfg, const char *repos_name,
+			      const char *path, const char *user,
+			      svn_repos_authz_access_t required_access,
+			      svn_boolean_t *granted_access,
+			      apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/tests/libsvn_repos/repos-test.c
===================================================================
--- subversion/tests/libsvn_repos/repos-test.c	(révision 15231)
+++ subversion/tests/libsvn_repos/repos-test.c	(copie de travail)
@@ -1,7 +1,7 @@
 /* repos-test.c --- tests for the filesystem
  *
  * ====================================================================
- * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+ * Copyright (c) 2000-2005 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
@@ -25,6 +25,8 @@
 #include "svn_repos.h"
 #include "svn_path.h"
 #include "svn_delta.h"
+#include "svn_config.h"
+#include "svn_io.h"
 
 #include "../svn_test.h"
 #include "../svn_test_fs.h"
@@ -1028,7 +1030,137 @@
 
   return SVN_NO_ERROR;
 }
+
 
+/* Test that authz is giving out the right authorizations */
+static svn_error_t *
+authz (const char **msg,
+       svn_boolean_t msg_only,
+       svn_test_opts_t *opts,
+       apr_pool_t *pool)
+{
+  const char *authz_path = "/tmp/authz.tmp";
+  const char *contents;
+  svn_config_t *cfg;
+  svn_boolean_t access_granted;
+  apr_pool_t *subpool = svn_pool_create (pool);
+  int i;
+  /* Definition of the paths to test and expected replies for each */
+  struct
+  {
+    const char *path;
+    const char *user;
+    const svn_repos_authz_access_t required;
+    const svn_boolean_t expected;
+  } test_set[] = {
+    { "/A", NULL, svn_authz_read, TRUE },
+    { "/iota", NULL, svn_authz_read, FALSE },
+    { "/A", "plato", svn_authz_write, TRUE },
+    { "/A", NULL, svn_authz_write, FALSE },
+    { "/A/B/lambda", "plato", svn_authz_read, TRUE },
+    { "/A/B/lambda", NULL, svn_authz_read, FALSE },
+    { "/A/C", NULL, svn_authz_read, TRUE },
+    { "/A/D", "plato", svn_authz_read | svn_authz_recursive, TRUE },
+    { "/A/D", NULL, svn_authz_read | svn_authz_recursive, FALSE },
+    { NULL, NULL, svn_authz_none, FALSE } /* sentinel */
+  };
+
+  *msg = "test authz access control";
+
+  if (msg_only)
+    return SVN_NO_ERROR;
+
+  /* The test logic: We dump a test authz file to disk, then load it
+   * and perform various access tests on it. Each test has a known
+   * outcome and tests different aspects of authz, such as inheriting
+   * parent-path authz, pan-repository rules or recursive access.
+   * 'plato' is our friendly neighborhood user with more access rights
+   * than other anonymous philosophers.
+   */
+
+  /* Output the authz file to disk */
+  contents =
+    "[greek:/A]"
+    APR_EOL_STR
+    "* = r"
+    APR_EOL_STR
+    "plato = w"
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/iota]"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR
+    "[/A/B/lambda]"
+    APR_EOL_STR
+    "plato = r"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/A/D]"
+    APR_EOL_STR
+    "plato = r"
+    APR_EOL_STR
+    "* = r"
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/A/D/G]"
+    APR_EOL_STR
+    "plato = r"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR
+    "[greek:/A/B/E/beta]"
+    APR_EOL_STR
+    "* ="
+    APR_EOL_STR
+    APR_EOL_STR;
+
+  SVN_ERR_W (svn_io_file_create (authz_path, contents, subpool),
+             "Writing test authz file");
+
+  /* Read the authz configuration back and start testing */
+  SVN_ERR_W (svn_config_read (&cfg, authz_path, TRUE, subpool),
+             "Opening test authz file");
+  SVN_ERR_W (svn_io_remove_file (authz_path, subpool),
+             "Removing test authz file");
+
+  for(i = 0; test_set[i].path != NULL; i++)
+    {
+      SVN_ERR (svn_repos_authz_check_access (cfg, "greek",
+                                             test_set[i].path,
+                                             test_set[i].user,
+                                             test_set[i].required,
+                                             &access_granted, subpool));
+
+      if (access_granted != test_set[i].expected)
+        {
+          return svn_error_createf (SVN_ERR_TEST_FAILED, NULL,
+                                    "Authz incorrectly %s %s%s access "
+                                    "to greek:%s for user %s",
+                                    access_granted ?
+                                    "grants" : "denies",
+                                    test_set[i].required
+                                    & svn_authz_recursive ?
+                                    "recursive " : "",
+                                    test_set[i].required
+                                    & svn_authz_read ?
+                                    "read" : "write",
+                                    test_set[i].path,
+                                    test_set[i].user ?
+                                    test_set[i].user : "-");
+        }
+    }
+
+  /* That's a wrap! */
+  svn_pool_destroy (subpool);
+  return SVN_NO_ERROR;
+}
+
+
 /* The test table.  */
 
 struct svn_test_descriptor_t test_funcs[] =
@@ -1039,5 +1171,6 @@
     SVN_TEST_PASS (revisions_changed),
     SVN_TEST_PASS (node_locations),
     SVN_TEST_PASS (rmlocks),
+    SVN_TEST_PASS (authz),
     SVN_TEST_NULL
   };
Index: subversion/libsvn_repos/authz.c
===================================================================
--- subversion/libsvn_repos/authz.c	(révision 0)
+++ subversion/libsvn_repos/authz.c	(révision 0)
@@ -0,0 +1,368 @@
+/* authz.c : path-based access control
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2005 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.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include <assert.h>
+
+#include <apr_pools.h>
+#include <apr_file_io.h>
+
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_path.h"
+#include "svn_repos.h"
+
+
+struct authz_baton {
+  apr_pool_t *pool;
+  svn_config_t *config;
+
+  const char *user;
+  svn_repos_authz_access_t allow;
+  svn_repos_authz_access_t deny;
+
+  svn_repos_authz_access_t required_access;
+  const char *repos_path;
+  const char *qualified_repos_path;
+
+  svn_boolean_t access;
+};
+
+
+
+/* Determine wether the required access is granted given what authz *
+ * are allowed and denied. Return TRUE if the required access is
+ * granted.
+ *
+ * Access is granted either when no required access is explicitely
+ * denied (implicit grant), or when the required access is explicitely
+ * granted, overriding any denials.
+ */
+static svn_boolean_t
+authz_access_is_granted (svn_repos_authz_access_t allow,
+                         svn_repos_authz_access_t deny,
+                         svn_repos_authz_access_t required)
+{
+  svn_repos_authz_access_t stripped_req =
+    required & (svn_authz_read | svn_authz_write);
+
+  if ((deny & required) == svn_authz_none)
+    return TRUE;
+  else if ((allow & required) == stripped_req)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+
+
+/* Decide wether the required access has been conclusively
+ * determined. Return TRUE if the given allow/deny authz are
+ * conclusive regarding the required authz.
+ *
+ * Conclusive determination occurs when any of the required authz are
+ * granted or denied by allow/deny.
+ */
+static svn_boolean_t
+authz_access_is_determined (svn_repos_authz_access_t allow,
+                            svn_repos_authz_access_t deny,
+                            svn_repos_authz_access_t required)
+{
+  if ((deny & required) || (allow & required))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+
+static svn_boolean_t
+authz_group_contains_user_internal (svn_config_t *cfg,
+                                    const char *group,
+                                    const char *user,
+                                    apr_hash_t *checked_groups,
+                                    apr_pool_t *pool)
+{
+  const char *value;
+  apr_array_header_t *list;
+  int i;
+
+  svn_config_get (cfg, &value, "groups", group, "");
+  list = svn_cstring_split (value, ",", TRUE, pool);
+
+  for (i = 0; i < list->nelts; i++)
+    {
+      const char *group_user = APR_ARRAY_IDX(list, i, char *);
+
+      /* If the 'user' is a subgroup, recurse into it */
+      if (*group_user == '@')
+        {
+          /* Guard against circular dependencies by checking group
+             name against hash. */
+          /* XXX: Should this return an error? */
+          if (apr_hash_get (checked_groups, &group_user[1],
+                            APR_HASH_KEY_STRING))
+            continue;
+
+          /* Add group to hash of checked groups */
+          apr_hash_set (checked_groups, &group_user[1],
+                        APR_HASH_KEY_STRING, "");
+
+          /* Recurse on that group */
+          if (authz_group_contains_user_internal (cfg, &group_user[1],
+                                                  user, checked_groups,
+                                                  pool))
+            return TRUE;
+        }
+      /* If the user matches, stop */
+      else if (strcmp (user, group_user) == 0)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+
+static svn_boolean_t
+authz_group_contains_user(svn_config_t *cfg, const char *group,
+                          const char *user, apr_pool_t *pool)
+{
+  return authz_group_contains_user_internal (cfg, group, user,
+                                             apr_hash_make (pool),
+                                             pool);
+}
+
+
+
+/*
+ * Callback to parse one line of an authz file and update the
+ * authz_baton accordingly.
+ */
+static svn_boolean_t
+authz_parse_line (const char *name, const char *value, void *baton)
+{
+  struct authz_baton *b = baton;
+
+  /* Work out wether this ACL line applies to the user */
+  if (strcmp (name, "*") != 0)
+    {
+      /* non-anon rule, anon user. Stop. */
+      if (!b->user)
+        return TRUE;
+
+      /* group rule and user not in group. Stop. */
+      if (*name == '@')
+        {
+          if (!authz_group_contains_user (b->config, &name[1],
+                                          b->user, b->pool))
+            return TRUE;
+        }
+      /* user rule for wrong user. Stop. */
+      else if (strcmp (name, b->user) != 0)
+        return TRUE;
+    }
+
+  /* Set the access grants for the rule */
+  if (strchr (value, 'r'))
+    b->allow |= svn_authz_read;
+  else
+    b->deny |= svn_authz_read;
+
+  if (strchr (value, 'w'))
+    b->allow |= svn_authz_write;
+  else
+    b->deny |= svn_authz_write;
+
+  return TRUE;
+}
+
+
+
+/*
+ * Callback to parse a section and update the authz_baton if the
+ * section denies access to the subtree the baton describes.
+ */
+static svn_boolean_t
+authz_parse_section (const char *section_name, void *baton)
+{
+  struct authz_baton *b = baton;
+  svn_boolean_t conclusive;
+
+  /* Does the section apply to us? */
+  if (strncmp (section_name, b->qualified_repos_path,
+               strlen (b->qualified_repos_path)) != 0
+      && strncmp (section_name, b->repos_path,
+                  strlen (b->repos_path)) != 0)
+    return TRUE;
+
+  /* Work out what this section grants */
+  b->allow = b->deny = 0;
+  svn_config_enumerate (b->config, section_name,
+                        authz_parse_line, b);
+
+  /* Has the section explicitely determined an access? */
+  conclusive = authz_access_is_determined (b->allow, b->deny,
+                                           b->required_access);
+
+  /* Is access granted OR inconclusive? */
+  b->access = authz_access_is_granted (b->allow, b->deny,
+                                       b->required_access)
+    || !conclusive;
+
+  /* As long as access isn't conclusively denied, carry on */
+  return b->access;
+}
+
+
+
+/*
+ * Validate access to the given user for the given path. This function
+ * checks rules for exactly the given path, and first tries to access
+ * a section specific to the given repository before falling back to
+ * pan-repository rules.
+ *
+ * Return a boolean which tells the caller wether we were able to
+ * determine the requested access rights. Access status is in
+ * *access_granted.
+ */
+static svn_boolean_t
+authz_get_path_access (svn_config_t *cfg, const char *repos_name,
+                       const char *path, const char *user,
+                       svn_repos_authz_access_t required_access,
+                       svn_boolean_t *access_granted,
+                       apr_pool_t *pool)
+{
+  const char *qualified_path;
+  struct authz_baton baton = { 0 };
+
+  baton.pool = pool;
+  baton.config = cfg;
+  baton.user = user;
+
+  /* Try to locate a repository-specific block first */
+  qualified_path = apr_pstrcat (pool, repos_name, ":", path, NULL);
+  svn_config_enumerate (cfg, qualified_path,
+                        authz_parse_line, &baton);
+
+  *access_granted = authz_access_is_granted (baton.allow, baton.deny,
+                                             required_access);
+
+  /* If the first test has determined access, stop now */
+  if (authz_access_is_determined (baton.allow, baton.deny,
+                                  required_access))
+    return TRUE;
+
+  /* No repository specific rule, try pan-repository rules */
+  svn_config_enumerate (cfg, path, authz_parse_line, &baton);
+
+  *access_granted = authz_access_is_granted (baton.allow, baton.deny,
+                                             required_access);
+  return authz_access_is_determined (baton.allow, baton.deny,
+                                     required_access);
+}
+
+
+
+/*
+ * Validate access to the given user for the subtree starting at the
+ * given path. This function walks the whole authz file in search of
+ * rules applying to paths in the requested subtree which deny the
+ * requested access.
+ *
+ * As soon as one is found, or else when the whole ACL file has been
+ * searched, return the updated authorization in access_granted.
+ */
+static void
+authz_get_tree_access (svn_config_t *cfg, const char *repos_name,
+                       const char *path, const char *user,
+                       svn_repos_authz_access_t required_access,
+                       svn_boolean_t *access_granted,
+                       apr_pool_t *pool)
+{
+  struct authz_baton baton = { 0 };
+
+  baton.pool = pool;
+  baton.config = cfg;
+  baton.user = user;
+  baton.required_access = required_access;
+  baton.repos_path = path;
+  baton.qualified_repos_path = apr_pstrcat (pool, repos_name,
+                                            ":", path, NULL);
+  /* Default to access granted if no rules say otherwise */
+  baton.access = TRUE;
+
+  svn_config_enumerate_sections (cfg, authz_parse_section, &baton);
+
+  *access_granted = baton.access;
+}
+
+
+
+svn_error_t *
+svn_repos_authz_check_access (svn_config_t *cfg, const char *repos_name,
+                              const char *path, const char *user,
+                              svn_repos_authz_access_t required_access,
+                              svn_boolean_t *access_granted,
+                              apr_pool_t *pool)
+{
+  const char *base_name = path;
+  const char *current_path = path;
+  svn_boolean_t access_determined = FALSE;
+
+  if (!path) {
+    /* XXX: Check if the user has 'required_access' _anywhere_ in the
+     * XXX: repository.  For now, make this always succeed, until
+     * XXX: we come up with a good way of figuring this out.
+     */
+    *access_granted = TRUE;
+    return SVN_NO_ERROR;
+  }
+
+  /* Determine the granted access for the requested path */
+  do
+    {
+      access_determined = authz_get_path_access (cfg, repos_name,
+                                                 current_path, user,
+                                                 required_access,
+                                                 access_granted, pool);
+
+      if (!access_determined)
+        {
+          /* Stop if the loop hits the repository root with no
+             results */
+          if (base_name[0] == '/' && base_name[1] == '\0')
+            {
+              /* Deny access by default */
+              *access_granted = FALSE;
+              return SVN_NO_ERROR;
+            }
+
+          /* Work back to the parent path */
+          svn_path_split (current_path, &current_path, &base_name, pool);
+        }
+    } while (!access_determined);
+
+  /* If the caller requested recursive access, we need to walk through
+     the entire authz config to see wether any child paths are denied
+     to the requested user. */
+  if (*access_granted
+      && (required_access & svn_authz_recursive))
+    authz_get_tree_access (cfg, repos_name, path, user,
+                           required_access, access_granted,
+                           pool);
+
+  return SVN_NO_ERROR;
+}


