=== notes/authz_policy.txt
==================================================================
--- notes/authz_policy.txt   (/online)   (revision 637)
+++ notes/authz_policy.txt   (/local/authz-in-svnserve)   (revision 637)
@@ -145,22 +145,29 @@
    1. Implement a read-authz callback (see svn_repos_authz_read_func_t), 
       and pass it to the following svn_repos.h functions:
    
+           svn_repos_begin_report()
            svn_repos_dir_delta()
-           svn_repos_get_logs2()
+           svn_repos_history2()
+           svn_repos_get_logs3()
            svn_repos_trace_node_locations()
            svn_repos_get_file_revs()
-           svn_repos_rev_prop()
-           svn_repos_rev_proplist()
            svn_repos_fs_get_locks()
+           svn_repos_fs_change_rev_prop2()
+           svn_repos_fs_revision_prop()
+           svn_repos_fs_revision_proplist()
+           svn_repos_get_commit_editor3()
    
-   2. Manually implement read-authz for incoming network requests that
+   2. Manually implement authz for incoming network requests that
       represent calls to:
    
            RA->get_file()
            RA->get_dir()
            RA->check_path()
+           RA->stat()
            RA->lock()
+           RA->lock_many()
            RA->unlock()
+           RA->unlock_many()
            RA->get_lock()
    
       (These concepts aren't wrapped by libsvn_repos because it's just
=== subversion/include/svn_config.h
==================================================================
--- subversion/include/svn_config.h   (/online)   (revision 637)
+++ subversion/include/svn_config.h   (/local/authz-in-svnserve)   (revision 637)
@@ -103,6 +103,7 @@
 #define SVN_CONFIG_OPTION_AUTH_ACCESS               "auth-access"
 #define SVN_CONFIG_OPTION_PASSWORD_DB               "password-db"
 #define SVN_CONFIG_OPTION_REALM                     "realm"
+#define SVN_CONFIG_OPTION_AUTHZ_DB                  "authz-db"
 
 /* For repository password database */
 #define SVN_CONFIG_SECTION_USERS                "users"
=== subversion/libsvn_repos/repos.c
==================================================================
--- subversion/libsvn_repos/repos.c   (/online)   (revision 637)
+++ subversion/libsvn_repos/repos.c   (/local/authz-in-svnserve)   (revision 637)
@@ -1392,6 +1392,20 @@
       APR_EOL_STR
       "# password-db = passwd"
       APR_EOL_STR
+      "### The authz-db option controls the location of the authorization"
+      APR_EOL_STR
+      "### rules for path-based access control.  Unless you specify a path"
+      APR_EOL_STR
+      "### starting with a /, the file's location is relative to the conf"
+      APR_EOL_STR
+      "### directory.  If you don't specify an authz-db, no path-based access"
+      APR_EOL_STR
+      "### control is done."
+      APR_EOL_STR
+      "### Uncomment the line below to use the default authorization file."
+      APR_EOL_STR
+      "# authz-db = authz"
+      APR_EOL_STR
       "### This option specifies the authentication realm of the repository."
       APR_EOL_STR
       "### If two repositories have the same authentication realm, they should"
@@ -1433,7 +1447,53 @@
                _("Creating passwd file"));
   }
 
+  {
+    static const char * const authz_contents =
+      "### This file is an example authorization file for svnserve."
+      APR_EOL_STR
+      "### Its format is similar to that of svnserve.conf.  As shown below,"
+      APR_EOL_STR
+      "### each section defines authorizations for the path and (optional)"
+      APR_EOL_STR
+      "### repository specified by the section name."
+      APR_EOL_STR
+      "### The authorizations follow. An authorization line can refer to a"
+      APR_EOL_STR
+      "### single user, to a group of users defined in a special [groups]"
+      APR_EOL_STR
+      "### section, or to anyone using the '*' wildcard.  Each definition can"
+      APR_EOL_STR
+      "### grant read ('r') access, read-write ('rw') access, or no access"
+      APR_EOL_STR
+      "### ('')."
+      APR_EOL_STR
+      APR_EOL_STR
+      "# [groups]"
+      APR_EOL_STR
+      "# harry_and_sally = harry,sally"
+      APR_EOL_STR
+      APR_EOL_STR
+      "# [/foo/bar]"
+      APR_EOL_STR
+      "# harry = rw"
+      APR_EOL_STR
+      "# * ="
+      APR_EOL_STR
+      APR_EOL_STR
+      "# [repository:/baz/fuz]"
+      APR_EOL_STR
+      "# @harry_and_sally = rw"
+      APR_EOL_STR
+      "# * = r"
+      APR_EOL_STR;
 
+    SVN_ERR_W (svn_io_file_create (svn_path_join (repos->conf_path,
+                                                  SVN_REPOS__CONF_AUTHZ,
+                                                  pool),
+                                   authz_contents, pool),
+               _("Creating authz file"));
+  }
+
   return SVN_NO_ERROR;
 }
 
=== subversion/libsvn_repos/repos.h
==================================================================
--- subversion/libsvn_repos/repos.h   (/online)   (revision 637)
+++ subversion/libsvn_repos/repos.h   (/local/authz-in-svnserve)   (revision 637)
@@ -70,6 +70,7 @@
 /* In the repository conf directory, look for these files. */
 #define SVN_REPOS__CONF_SVNSERVE_CONF "svnserve.conf"
 #define SVN_REPOS__CONF_PASSWD "passwd"
+#define SVN_REPOS__CONF_AUTHZ "authz"
 
 /* The Repository object, created by svn_repos_open() and
    svn_repos_create(), allocated in POOL. */
=== subversion/svnserve/serve.c
==================================================================
--- subversion/svnserve/serve.c   (/online)   (revision 637)
+++ subversion/svnserve/serve.c   (/local/authz-in-svnserve)   (revision 637)
@@ -47,6 +47,8 @@
   svn_fs_t *fs;            /* For convenience; same as svn_repos_fs(repos) */
   svn_config_t *cfg;       /* Parsed repository svnserve.conf */
   svn_config_t *pwdb;      /* Parsed password database */
+  svn_authz_t *authzdb;    /* Parsed authz rules */
+  const char *authz_repos_name; /* The name of the repository */
   const char *realm;       /* Authentication realm */
   const char *repos_url;   /* URL to base of repository */
   const char *fs_path;     /* Decoded base path inside repository */
@@ -102,6 +104,73 @@
 
 /* --- AUTHENTICATION AND AUTHORIZATION FUNCTIONS --- */
 
+/* Set *ALLOWED to TRUE if PATH is accessible in the REQUIRED mode to
+   the user described in BATON according to the authz rules in BATON.
+   Use POOL for temporary allocations only.  If no authz rules are
+   present in BATON, grant access by default. */
+static svn_error_t *authz_check_access(svn_boolean_t *allowed,
+                                       const char *path,
+                                       svn_repos_authz_access_t required,
+                                       server_baton_t *b,
+                                       apr_pool_t *pool)
+{
+  /* If authz cannot be performed, grant access.  This is NOT the same
+     as the default policy when authz is performed on a path with no
+     rules.  In the latter case, the default is to deny access, and is
+     set by svn_repos_authz_check_access. */
+  if (!b->authzdb || !path)
+    {
+      *allowed = TRUE;
+      return SVN_NO_ERROR;
+    }
+
+  /* If the authz request is for the empty path (ie. ""), replace it
+     with the root path.  This happens because of stripping done at
+     various levels in svnserve that remove the leading / on an
+     absolute path. Passing such a malformed path to the authz
+     routines throws them into an infinite loop and makes them miss
+     ACLs. */
+  if (*path == '\0')
+    path = "/";
+
+  return svn_repos_authz_check_access(b->authzdb, b->authz_repos_name,
+                                      path, b->user, required,
+                                      allowed, pool);
+}
+
+/* Set *ALLOWED to TRUE if PATH is readable by the user described in
+ * BATON.  Use POOL for temporary allocations only.  ROOT is not used.
+ * Implements the svn_repos_authz_func_t interface.
+ */
+static svn_error_t *authz_check_access_cb(svn_boolean_t *allowed,
+                                          svn_fs_root_t *root,
+                                          const char *path,
+                                          void *baton,
+                                          apr_pool_t *pool)
+{
+  server_baton_t *sb = baton;
+
+  return authz_check_access(allowed, path, svn_authz_read, sb, pool);
+}
+
+/* Set *ALLOWED to TRUE if the REQUIRED access to PATH is granted,
+ * according to the state in BATON.  Use POOL for temporary
+ * allocations only.  ROOT is not used.  Implements the
+ * svn_repos_authz_callback_t interface.
+ */
+static svn_error_t *authz_commit_cb(svn_repos_authz_access_t required,
+                                    svn_boolean_t *allowed,
+                                    svn_fs_root_t *root,
+                                    const char *path,
+                                    void *baton,
+                                    apr_pool_t *pool)
+{
+  server_baton_t *sb = baton;
+
+  return authz_check_access(allowed, path, required, sb, pool);
+}
+
+
 static enum access_type get_access(server_baton_t *b, enum authn_type auth)
 {
   const char *var = (auth == AUTHENTICATED) ? SVN_CONFIG_OPTION_AUTH_ACCESS :
@@ -268,31 +337,103 @@
   return svn_ra_svn_write_cmd_response(conn, pool, "()c", "");
 }
 
-/* Ensure that the client has write access.  If the client already has
-   write access, just send a trivial auth request.  Else, try to authenticate
-   the client.  If NEEDS_USERNAME is TRUE, only use auth mechs that will yield
-   a username.  Return an error if write access couldn't be achieved. */
-static svn_error_t *must_have_write_access(svn_ra_svn_conn_t *conn,
-                                           apr_pool_t *pool, server_baton_t *b,
-                                           svn_boolean_t needs_username)
+/* Ensure that the client has the REQUIRED access by checking the
+ * access directives (both blanket and per-directory) in BATON.  If
+ * PATH is NULL, then only the blanket access configuration will
+ * impact the result.
+ *
+ * If NEEDS_USERNAME is TRUE, then a lookup is only successful if the
+ * user described in BATON is authenticated and, well, has a username
+ * assigned to him.
+ *
+ * Use POOL for temporary allocations only.
+ */
+static svn_boolean_t lookup_access(apr_pool_t *pool,
+                                   server_baton_t *baton,
+                                   svn_repos_authz_access_t required,
+                                   const char *path,
+                                   svn_boolean_t needs_username)
 {
-  if (current_access(b) == WRITE_ACCESS
-      && (! needs_username || b->user))
+  enum access_type req = (required & svn_authz_write) ?
+    WRITE_ACCESS : READ_ACCESS;
+  svn_boolean_t authorized;
+  svn_error_t *err;
+
+  /* Get authz's opinion on the access. */
+  err = authz_check_access(&authorized, path, required, baton, pool);
+
+  /* If an error made lookup fail, deny access.  ### TODO: Once
+     logging is implemented, this is a perfect place to log the
+     problem. */
+  if (err)
     {
+      svn_error_clear(err);
+      return FALSE;
+    }
+
+  /* If the required access is blanket-granted AND granted by authz
+     AND we already have a username if one is required, then the
+     lookup has succeeded. */
+  if (current_access(baton) >= req
+      && authorized
+      && (! needs_username || baton->user))
+    return TRUE;
+
+  return FALSE;
+}
+
+/* Check that the client has the REQUIRED access by consulting the
+ * authentication and authorization states stored in BATON.  If the
+ * client does not have the required access credentials, attempt to
+ * authenticate the client to get that access, using CONN for
+ * communication.
+ *
+ * This function is supposed to be called to handle the authentication
+ * half of a standard svn protocol reply.  If an error is returned, it
+ * probably means that the server can terminate the client connection
+ * with an apologetic error, as it implies an authentication failure.
+ *
+ * PATH and NEEDS_USERNAME are passed along to lookup_access, their
+ * behaviour is documented there.
+ */
+static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn,
+                                     apr_pool_t *pool,
+                                     server_baton_t *b,
+                                     svn_repos_authz_access_t required,
+                                     const char *path,
+                                     svn_boolean_t needs_username)
+{
+  enum access_type req = (required & svn_authz_write) ?
+    WRITE_ACCESS : READ_ACCESS;
+
+  /* See whether the user already has the required access.  If so,
+     nothing needs to be done.  Create the FS access and send a
+     trivial auth request. */
+  if (lookup_access(pool, b, required, path, needs_username))
+    {
       SVN_ERR(create_fs_access(b, pool));
       return trivial_auth_request(conn, pool, b);
     }
 
-  /* If we can get write access by authenticating, try that. */
-  if (b->user == NULL && get_access(b, AUTHENTICATED) == WRITE_ACCESS
+  /* If the required blanket access can be obtained by authenticating,
+     try that.  Unfortunately, we can't tell until after
+     authentication whether authz will work or not.  We force
+     requiring a username because we need one to be able to check
+     authz configuration again with a different user credentials than
+     the first time round. */
+  if (b->user == NULL
+      && get_access(b, AUTHENTICATED) >= req
       && (b->tunnel_user || b->pwdb) && b->protocol_version >= 2)
-    SVN_ERR(auth_request(conn, pool, b, WRITE_ACCESS, needs_username));
+    SVN_ERR(auth_request(conn, pool, b, req, TRUE));
 
-  if (current_access(b) != WRITE_ACCESS)
+  /* Now that an authentication has been done get the new take of
+     authz on the request. */
+  if (! lookup_access(pool, b, required, path, needs_username))
     return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
-                            svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
-                                             "Connection is read-only"), NULL);
+                            svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED,
+                                             NULL, NULL), NULL);
 
+  /* Else, access is granted, and there is much rejoicing. */
   SVN_ERR(create_fs_access(b, pool));
 
   return SVN_NO_ERROR;
@@ -409,7 +550,7 @@
   SVN_CMD_ERR(svn_repos_begin_report(&report_baton, rev, b->user, b->repos,
                                      b->fs_path, target, tgt_path, text_deltas,
                                      recurse, ignore_ancestry, editor,
-                                     edit_baton, NULL, NULL, pool));
+                                     edit_baton, authz_check_access_cb, b, pool));
 
   rb.sb = b;
   rb.repos_url = svn_path_uri_decode(b->repos_url, pool);
@@ -581,9 +722,11 @@
   svn_string_t *value;
 
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "rc?s", &rev, &name, &value));
-  SVN_ERR(must_have_write_access(conn, pool, b, FALSE));
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE));
   SVN_CMD_ERR(svn_repos_fs_change_rev_prop2(b->repos, rev, b->user,
-                                            name, value, NULL, NULL, pool));
+                                            name, value,
+                                            authz_check_access_cb, b,
+                                            pool));
   SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
   return SVN_NO_ERROR;
 }
@@ -598,7 +741,8 @@
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "r", &rev));
   SVN_ERR(trivial_auth_request(conn, pool, b));
   SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev,
-                                              NULL, NULL, pool));
+                                             authz_check_access_cb, b,
+                                             pool));
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success"));
   SVN_ERR(write_proplist(conn, pool, props));
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))"));
@@ -616,7 +760,7 @@
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "rc", &rev, &name));
   SVN_ERR(trivial_auth_request(conn, pool, b));
   SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repos, rev, name,
-                                         NULL, NULL, pool));
+                                         authz_check_access_cb, b, pool));
   SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "(?s)", value));
   return SVN_NO_ERROR;
 }
@@ -632,10 +776,15 @@
   return SVN_NO_ERROR;
 }
 
-/* Add the LOCK_TOKENS to the filesystem access context if any. LOCK_TOKENS is
-   an array of svn_ra_svn_item_t structs.  Return an error if they are
-   not a list of lists. */
-static svn_error_t *add_lock_tokens(apr_array_header_t *lock_tokens,
+/* Add the LOCK_TOKENS (if any) to the filesystem access context,
+ * checking path authorizations using the state in SB as we go.
+ * LOCK_TOKENS is an array of svn_ra_svn_item_t structs.  Return a
+ * client error if LOCK_TOKENS is not a list of lists.  If a lock
+ * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED
+ * to the client.  Use POOL for temporary allocations only.
+ */
+static svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn,
+                                    apr_array_header_t *lock_tokens,
                                     server_baton_t *sb,
                                     apr_pool_t *pool)
 {
@@ -661,13 +810,18 @@
       path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
       if (path_item->kind != SVN_RA_SVN_STRING)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
-                                "Lock path isn't a string.");
+                                "Lock path isn't a string");
 
       token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
       if (token_item->kind != SVN_RA_SVN_STRING)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 "Lock token isn't a string");
 
+      if (! lookup_access(pool, sb, svn_authz_write,
+                          path_item->u.string->data, TRUE))
+        return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED,
+                                NULL, NULL);
+
       token = token_item->u.string->data;
       SVN_ERR(svn_fs_access_add_lock_token(fs_access, token));
     }
@@ -741,13 +895,20 @@
   else
     SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "clb", &log_msg,
                                    &lock_tokens, &keep_locks));
-  /* Require a username if the client gave us any lock tokens. */
-  SVN_ERR(must_have_write_access(conn, pool, b,
-                                 lock_tokens && lock_tokens->nelts > 0));
 
-  /* Give the lock tokens to the FS if we got any. */
+  /* The handling for locks is a little problematic, because the
+     protocol won't let us send several auth requests once one has
+     succeeded.  So we request write access and a username before
+     adding tokens (if we have any), and subsequently fail if a lock
+     violates authz. */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
+                           NULL, lock_tokens ? TRUE : FALSE));
+
+  /* Authorize the lock tokens and give them to the FS if we got
+     any. */
   if (lock_tokens)
-    SVN_CMD_ERR(add_lock_tokens(lock_tokens, b, pool));
+    SVN_CMD_ERR(add_lock_tokens(conn, lock_tokens, b, pool));
+
   ccb.new_rev = &new_rev;
   ccb.date = &date;
   ccb.author = &author;
@@ -756,7 +917,8 @@
               (&editor, &edit_baton, b->repos, NULL,
                svn_path_uri_decode(b->repos_url, pool),
                b->fs_path, b->user,
-               log_msg, commit_done, &ccb, NULL, NULL, pool));
+               log_msg, commit_done, &ccb,
+               authz_commit_cb, baton, pool));
   SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
   SVN_ERR(svn_ra_svn_drive_editor(conn, pool, editor, edit_baton, &aborted));
   if (!aborted)
@@ -803,11 +965,16 @@
   /* Parse arguments. */
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)bb", &path, &rev,
                                  &want_props, &want_contents));
-  path = svn_path_canonicalize(path, pool);
-  SVN_ERR(trivial_auth_request(conn, pool, b));
+
+  full_path = svn_path_join(b->fs_path,
+                            svn_path_canonicalize(path, pool), pool);
+
+  /* Check authorizations */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
+                           full_path, FALSE));
+
   if (!SVN_IS_VALID_REVNUM(rev))
     SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
-  full_path = svn_path_join(b->fs_path, path, pool);
 
   /* Fetch the properties and a stream for the contents. */
   SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
@@ -877,11 +1044,16 @@
 
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)bb", &path, &rev,
                                  &want_props, &want_contents));
-  path = svn_path_canonicalize(path, pool);
-  SVN_ERR(trivial_auth_request(conn, pool, b));
+
+  full_path = svn_path_join(b->fs_path,
+                            svn_path_canonicalize(path, pool), pool);
+
+  /* Check authorizations */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
+                           full_path, FALSE));
+
   if (!SVN_IS_VALID_REVNUM(rev))
     SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
-  full_path = svn_path_join(b->fs_path, path, pool);
 
   /* Fetch the root of the appropriate revision. */
   SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
@@ -1133,7 +1305,8 @@
   lb.conn = conn;
   err = svn_repos_get_logs3(b->repos, full_paths, start_rev, end_rev,
                             (int) limit, changed_paths, strict_node,
-                            NULL, NULL, log_receiver, &lb, pool);
+                            authz_check_access_cb, b, log_receiver,
+                            &lb, pool);
 
   write_err = svn_ra_svn_write_word(conn, pool, "done");
   if (write_err)
@@ -1156,11 +1329,16 @@
   svn_node_kind_t kind;
 
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)", &path, &rev));
-  path = svn_path_canonicalize(path, pool);
-  SVN_ERR(trivial_auth_request(conn, pool, b));
+  full_path = svn_path_join(b->fs_path,
+                            svn_path_canonicalize(path, pool), pool);
+
+  /* Check authorizations */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
+                           full_path, FALSE));
+
   if (!SVN_IS_VALID_REVNUM(rev))
     SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
-  full_path = svn_path_join(b->fs_path, path, pool);
+
   SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
   SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool));
   SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "w", kind_word(kind)));
@@ -1177,11 +1355,15 @@
   svn_dirent_t *dirent;
 
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)", &path, &rev));
-  path = svn_path_canonicalize(path, pool);
-  SVN_ERR(trivial_auth_request(conn, pool, b));
+  full_path = svn_path_join(b->fs_path,
+                            svn_path_canonicalize(path, pool), pool);
+
+  /* Check authorizations */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
+                           full_path, FALSE));
+
   if (!SVN_IS_VALID_REVNUM(rev))
     SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
-  full_path = svn_path_join(b->fs_path, path, pool);
   SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
   SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool));
 
@@ -1253,7 +1435,7 @@
 
   err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path,
                                        peg_revision, location_revisions,
-                                       NULL, NULL, pool);
+                                       authz_check_access_cb, b, pool);
 
   /* Now, write the results to the connection. */
   if (!err)
@@ -1363,8 +1545,9 @@
   frb.conn = conn;
   frb.pool = NULL;
 
-  err = svn_repos_get_file_revs(b->repos, full_path, start_rev, end_rev, NULL,
-                                NULL, file_rev_handler, &frb, pool);
+  err = svn_repos_get_file_revs(b->repos, full_path, start_rev, end_rev,
+                                authz_check_access_cb, b, file_rev_handler,
+                                &frb, pool);
   write_err = svn_ra_svn_write_word(conn, pool, "done");
   if (write_err)
     {
@@ -1390,12 +1573,12 @@
 
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment,
                                  &steal_lock, &current_rev));
+  full_path = svn_path_join(b->fs_path,
+                            svn_path_canonicalize(path, pool), pool);
 
-  full_path = svn_path_join(b->fs_path, svn_path_canonicalize(path, pool),
-                            pool);
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
+                           full_path, TRUE));
 
-  SVN_ERR(must_have_write_access(conn, pool, b, TRUE));
-
   SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repos, full_path, NULL, comment, 0,
                                 0, /* No expiration time. */
                                 current_rev, steal_lock, pool));
@@ -1429,30 +1612,37 @@
   subpool = svn_pool_create(pool);
   lock_cmds = apr_array_make(pool, locks->nelts, sizeof(struct lock_cmd));
 
+  /* Because we can only send a single auth reply per request, we send
+     a reply before parsing the lock commands.  This means an authz
+     access denial will abort the processing of the locks and return
+     an error. */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE));
+
   /* Loop through the lock commands. */
   for (i = 0; i < locks->nelts; ++i)
     {
       struct lock_cmd *cmd = apr_pcalloc(pool, sizeof(struct lock_cmd));
       svn_ra_svn_item_t *item = &APR_ARRAY_IDX(locks, i, svn_ra_svn_item_t);
 
-      svn_pool_clear(subpool);
-
       if (item->kind != SVN_RA_SVN_LIST)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 "Lock commands should be list of lists\n");
 
-      SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, subpool, "c(?r)",
+      SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, pool, "c(?r)",
                                      &cmd->path, &cmd->current_rev));
 
       cmd->full_path = svn_path_join(b->fs_path,
                                      svn_path_canonicalize(cmd->path, subpool),
                                      pool);
 
+      if (! lookup_access(pool, b, svn_authz_write, cmd->full_path, TRUE))
+        return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
+                                svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED,
+                                                 NULL, NULL), NULL);
+
       APR_ARRAY_PUSH(lock_cmds, struct lock_cmd) = *cmd;
     }
 
-  SVN_ERR(must_have_write_access(conn, pool, b, TRUE));
-
   /* Loop through each path to be locked. */
   for (i = 0; i < lock_cmds->nelts; i++)
     {
@@ -1496,8 +1686,9 @@
   full_path = svn_path_join(b->fs_path, svn_path_canonicalize(path, pool),
                             pool);
 
-  /* Username required unless break-lock was specified. */
-  SVN_ERR(must_have_write_access(conn, pool, b, ! break_lock));
+  /* Username required unless force was specified. */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
+                           full_path, ! break_lock));
 
   SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
                                   pool));
@@ -1527,6 +1718,9 @@
   unlock_cmds =
     apr_array_make(pool, unlock_tokens->nelts, sizeof(struct unlock_cmd));
 
+  /* Username required unless force was specified. */
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock));
+
   subpool = svn_pool_create(pool);
   /* Loop through the unlock commands. */
   for (i = 0; i < unlock_tokens->nelts; i++)
@@ -1548,12 +1742,14 @@
                                      svn_path_canonicalize(cmd->path, subpool),
                                      pool);
 
+      if (! lookup_access(pool, b, svn_authz_write, cmd->full_path, ! force))
+        return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
+                                svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED,
+                                                 NULL, NULL), NULL);
+
       APR_ARRAY_PUSH(unlock_cmds, struct unlock_cmd) = *cmd;
     }
 
-  /* Username required unless break-lock was specified. */
-  SVN_ERR(must_have_write_access(conn, pool, b, ! break_lock));
-
   /* Loop through each path to be unlocked. */
   for (i = 0; i < unlock_cmds->nelts; i++)
     {
@@ -1585,7 +1781,8 @@
   full_path = svn_path_join(b->fs_path, svn_path_canonicalize(path, pool),
                             pool);
 
-  SVN_ERR(trivial_auth_request(conn, pool, b));
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
+                           full_path, FALSE));
 
   SVN_CMD_ERR(svn_fs_get_lock(&l, b->fs, full_path, pool));
 
@@ -1617,7 +1814,7 @@
   SVN_ERR(trivial_auth_request(conn, pool, b));
   
   SVN_CMD_ERR(svn_repos_fs_get_locks(&locks, b->repos, full_path, 
-                                     NULL, NULL, pool));
+                                     authz_check_access_cb, b, pool));
 
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((!", "success"));
   for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
@@ -1717,7 +1914,7 @@
 static svn_error_t *find_repos(const char *url, const char *root,
                                server_baton_t *b, apr_pool_t *pool)
 {
-  const char *path, *full_path, *repos_root, *pwdb_path;
+  const char *path, *full_path, *repos_root, *pwdb_path, *authz_path;
   svn_stringbuf_t *url_buf;
 
   /* Skip past the scheme and authority part. */
@@ -1756,6 +1953,7 @@
   url_buf = svn_stringbuf_create(url, pool);
   svn_path_remove_components(url_buf, svn_path_component_count(b->fs_path));
   b->repos_url = url_buf->data;
+  b->authz_repos_name = svn_path_basename(repos_root, pool);
 
   /* Read repository configuration. */
   SVN_ERR(svn_config_read(&b->cfg, svn_repos_svnserve_conf(b->repos, pool),
@@ -1779,7 +1977,25 @@
       b->realm = "";
     }
 
-  /* Make sure it's possible for the client to authenticate. */
+  /* Read authz configuration. */
+  svn_config_get(b->cfg, &authz_path, SVN_CONFIG_SECTION_GENERAL,
+                 SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
+  if (authz_path)
+    {
+      authz_path = svn_path_join(svn_repos_conf_dir(b->repos, pool),
+                                 authz_path, pool);
+      SVN_ERR(svn_repos_authz_read(&b->authzdb, authz_path,
+                                   TRUE, pool));
+    }
+  else
+    {
+      b->authzdb = NULL;
+    }
+
+  /* Make sure it's possible for the client to authenticate.  Note
+     that this doesn't take into account any the authz configuration
+     read above, because we can't know about access it grants until
+     paths are given by the client. */
   if (get_access(b, UNAUTHENTICATED) == NO_ACCESS
       && (get_access(b, AUTHENTICATED) == NO_ACCESS
           || (!b->tunnel_user && !b->pwdb)))

Property changes on: 
___________________________________________________________________
Name: svk:merge
  40bec6f9-eef0-0310-8400-9b45a50af7ca:/local/trunk:90
  422c9f7f-73e9-0310-916f-f579f4d99a6a:/local/pool-cleanup:1951
 +65390229-12b7-0310-b90b-f21a5aa7ec8e:/trunk:15944
  ae6c956b-9dc6-0310-97b2-e73af4192982:/svn/local:8265
  c9b7866f-f5e4-0310-b3ee-a2e1643c603f:/svn/local/new-auto-merge:10428



