=== notes/authz_policy.txt
==================================================================
--- notes/authz_policy.txt   (/online)   (revision 443)
+++ notes/authz_policy.txt   (/local/authz-in-svnserve)   (revision 443)
@@ -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 443)
+++ subversion/include/svn_config.h   (/local/authz-in-svnserve)   (revision 443)
@@ -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 443)
+++ subversion/libsvn_repos/repos.c   (/local/authz-in-svnserve)   (revision 443)
@@ -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 443)
+++ subversion/libsvn_repos/repos.h   (/local/authz-in-svnserve)   (revision 443)
@@ -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 443)
+++ subversion/svnserve/serve.c   (/local/authz-in-svnserve)   (revision 443)
@@ -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 *repos_name;  /* The name of the repository (for authz) */
   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,74 @@
 
 /* --- 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 to emulate complete
+   absence of authz code. */
+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->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.
+ *
+ * This is passed as a callback to many functions of svn_repos.h to
+ * check authz read access. */
+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.
+ *
+ * This is passed as a callback to the commit editor. */
+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 +338,91 @@
   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
+ * blanket access directives and possibly authz directives if PATH is
+ * not NULL.
+ */
+static svn_boolean_t lookup_access(apr_pool_t *pool,
+                                   server_baton_t *b,
+                                   svn_repos_authz_access_t required,
+                                   const char *path,
+                                   svn_boolean_t needs_username)
 {
-  if (current_access(b) == WRITE_ACCESS
+  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.  If authz is disabled in the
+     server config, this will just allow access blindly.  This will
+     make the function only consider blanket access rules when
+     deciding what to do. */
+  err = authz_check_access(&authorized, path, required, b, 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 the request complies with NEEDS_USERNAME
+     restrictions, then the lookup has succeeded. */
+  if (current_access(b) >= req
+      && authorized
       && (! needs_username || b->user))
+    return TRUE;
+
+  return FALSE;
+}
+
+/* Check that the client has the REQUIRED_ACCESS by using
+ * lookup_access.  If access is not granted, try to authenticate the
+ * client to get access.  Otherwise, just send a trivial auth request
+ * and be done.
+ *
+ */
+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 here so that the ANONYMOUS mechanism (which
+     obviously won't work) doesn't get sent here. */
+  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 (if using protocol
+     version 2 that is), 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 +539,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 +711,12 @@
   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_write_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 +731,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 +750,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,17 +766,19 @@
   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 as we go.  LOCK_TOKENS is an array of
+   svn_ra_svn_item_t structs.  Return a client error if LOCK_TOKENS
+   are not a list of lists or if a lock violates authz. */
+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)
 {
   int i;
   svn_fs_access_t *fs_access;
 
-  SVN_ERR(svn_fs_get_access(&fs_access, sb->fs));
+  SVN_CMD_ERR(svn_fs_get_access(&fs_access, sb->fs));
 
   /* If there is no access context, nowhere to add the tokens. */
   if (! fs_access)
@@ -658,18 +794,17 @@
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 "Lock tokens aren't a list of lists");
 
-      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.");
+      SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, pool, "ss",
+                                     &path_item, &token_item));
 
-      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_SVN_CMD_ERR,
+                                svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED,
+                                                 NULL, NULL), NULL);
 
       token = token_item->u.string->data;
-      SVN_ERR(svn_fs_access_add_lock_token(fs_access, token));
+      SVN_CMD_ERR(svn_fs_access_add_lock_token(fs_access, token));
     }
 
   return SVN_NO_ERROR;
@@ -741,13 +876,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 simply 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_ERR(add_lock_tokens(conn, lock_tokens, b, pool));
+
   ccb.new_rev = &new_rev;
   ccb.date = &date;
   ccb.author = &author;
@@ -756,7 +898,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 +946,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 +1025,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 +1286,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 +1310,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 +1336,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 +1416,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 +1526,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 +1554,12 @@
 
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment,
                                  &force, &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,  force, pool));
@@ -1429,6 +1593,12 @@
   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, subpool, b, svn_authz_write, NULL, TRUE));
+
   /* Loop through the lock commands. */
   for (i = 0; i < locks->nelts; ++i)
     {
@@ -1448,11 +1618,14 @@
                                      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++)
     {
@@ -1497,7 +1670,8 @@
                             pool);
 
   /* Username required unless force was specified. */
-  SVN_ERR(must_have_write_access(conn, pool, b, ! force));
+  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
+                           full_path, ! force));
 
   SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, force, pool));
 
@@ -1525,6 +1699,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, ! force));
+
   subpool = svn_pool_create(pool);
   /* Loop through the unlock commands. */
   for (i = 0; i < unlock_tokens->nelts; i++)
@@ -1546,12 +1723,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 force was specified. */
-  SVN_ERR(must_have_write_access(conn, pool, b, ! force));
-
   /* Loop through each path to be unlocked. */
   for (i = 0; i < unlock_cmds->nelts; i++)
     {
@@ -1583,7 +1762,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));
 
@@ -1615,7 +1795,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))
@@ -1715,7 +1895,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. */
@@ -1754,6 +1934,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->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),
@@ -1777,7 +1958,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 authz, 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:15885
  ae6c956b-9dc6-0310-97b2-e73af4192982:/svn/local:8265
  c9b7866f-f5e4-0310-b3ee-a2e1643c603f:/svn/local/new-auto-merge:10428



