Only in svn-r21200-native-authz/: .vimrc diff -ru svn-trunk-r21200/subversion/mod_dav_svn/authz.c svn-r21200-native-authz/subversion/mod_dav_svn/authz.c --- svn-trunk-r21200/subversion/mod_dav_svn/authz.c 2006-08-07 20:48:05.000000000 +0300 +++ svn-r21200-native-authz/subversion/mod_dav_svn/authz.c 2006-08-24 00:50:41.000000000 +0300 @@ -22,8 +22,15 @@ #include "svn_pools.h" #include "svn_path.h" +#include "svn_dav.h" #include "dav_svn.h" /* Convert incoming REV and PATH from request R into a version-resource URI for REPOS and perform a GET subrequest on it. This will invoke any authz @@ -58,15 +65,31 @@ /* Build a Version Resource uri representing (rev, path). */ uri = dav_svn__build_uri(repos, uri_type, rev, path, FALSE, pool); - /* Check if GET would work against this uri. */ - subreq = ap_sub_req_method_uri("GET", uri, r, r->output_filters); - - if (subreq) + if (dav_svn__get_native_authz_flag(r)) { - if (subreq->status == HTTP_OK) + /* Do native auhorization lookup - read access */ + dav_error *err = dav_svn__check_access(repos->repo_name, + path, + r, + svn_authz_read); + + if (! err) allowed = TRUE; - ap_destroy_sub_req(subreq); + /* XXX: need to cleanup dav_error? */ + } + else + { + /* Check if GET would work against this uri. */ + subreq = ap_sub_req_method_uri("GET", uri, r, r->output_filters); + + if (subreq) + { + if (subreq->status == HTTP_OK) + allowed = TRUE; + + ap_destroy_sub_req(subreq); + } } return allowed; @@ -185,3 +208,201 @@ return allow_read(resource->info->r, resource->info->repos, resource->info->repos_path, rev, pool); } + +/* Native path-based authorization */ + +static int +check_access(const char *repos_name, + const char *repos_path, + request_rec* r, + svn_repos_authz_access_t required_access) +{ + const char *authz_file = NULL; + svn_authz_t *access_conf = NULL; + svn_error_t *svn_err; + dav_error *dav_err; + const char *cache_key; + void *user_data; + svn_boolean_t access_granted; + char errbuf[128]; + + /* If native authz is off, there's nothing to do. Return DONE + * instead of OK to indicate that no checks have really been done. + */ + if (!dav_svn__get_native_authz_flag(r)) + return DONE; + + authz_file = dav_svn__get_native_authz_file(r); + /* If access file had not been specified, the default + behavior is to allow access. + XXX: is this right? */ + if(authz_file == NULL) + return OK; + + /* Retrieve/cache authorization file */ + cache_key = apr_pstrcat(r->pool, "mod_dav_svn:", authz_file, NULL); + apr_pool_userdata_get(&user_data, cache_key, r->connection->pool); + access_conf = user_data; + if (access_conf == NULL) { + svn_err = svn_repos_authz_read(&access_conf, authz_file, + TRUE, r->connection->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 load the SVNNativeAuthzFile: %s", + svn_err_best_message(svn_err, + errbuf, sizeof(errbuf))); + svn_error_clear(svn_err); + + return DECLINED; + } + + /* Cache the open repos for the next request on this connection */ + apr_pool_userdata_set(access_conf, cache_key, + NULL, r->connection->pool); + } + + /* Perform authz access control. */ + svn_err = svn_repos_authz_check_access(access_conf, repos_name, + repos_path, r->user, + required_access, + &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_best_message(svn_err, errbuf, sizeof(errbuf))); + svn_error_clear(svn_err); + + return DECLINED; + } + + if (!access_granted) + return DECLINED; + + return OK; +} + +/* Log a message indicating the access control decision made about a + * request. FILE and LINE should be supplied via the APLOG_MARK macro. + * ALLOWED is boolean. REPOS_PATH and DEST_REPOS_PATH are information + * about the request. DEST_REPOS_PATH may be NULL. */ +static void +log_access_verdict(const char *file, int line, + const request_rec *r, + int allowed, + const char *repos_path, + svn_repos_authz_access_t required_access) +{ + int level = allowed ? APLOG_INFO : APLOG_ERR; + const char *verdict = allowed ? "granted" : "denied"; + + char access_str[4] = { 0, 0, 0, 0 }; + int access_idx = 0; + + if (required_access & svn_authz_read) + access_str[access_idx++] = 'r'; + + if (required_access & svn_authz_write) + access_str[access_idx++] = 'w'; + + if (required_access & svn_authz_recursive) + access_str[access_idx++] = 'R'; + + if (repos_path == NULL) + repos_path = ""; + + if (r->user) + { + ap_log_rerror(file, line, level, 0, r, + "[native] Access %s: '%s' %s %s %s", verdict, r->user, + r->method, repos_path, access_str); + } + else + { + ap_log_rerror(file, line, level, 0, r, + "[native] Access %s: - %s %s %s", verdict, + r->method, repos_path, access_str); + } +} + +dav_error * +dav_svn__check_access(const char *repos_name, + const char *repos_path, + request_rec *r, + svn_repos_authz_access_t required_access) +{ + int status; + + status = check_access(repos_name, repos_path, r, required_access); + + /* If no checks had been done, native authz is off, so don't log + * a possibly misleading authorization verdict. + */ + if (status == DONE) + return NULL; + + if(status == DECLINED) + { + log_access_verdict(APLOG_MARK, r, 0, repos_path, required_access); + ap_note_auth_failure(r); // XXX: need this? + + // XXX: need better error message + return dav_svn__new_error_tag(r->pool, HTTP_FORBIDDEN, 0, + "Insufficient rights to access resource.", + SVN_DAV_ERROR_NAMESPACE, + SVN_DAV_ERROR_TAG); + } + + log_access_verdict(APLOG_MARK, r, 1, repos_path, required_access); + + return NULL; +} + +dav_error * +dav_svn__check_resource_access(const dav_resource *resource, + const svn_repos_authz_access_t required_access) +{ + return dav_svn__check_access(resource->info->repos->repo_name, + resource->info->repos_path, + resource->info->r, + required_access); +} + +dav_error * +dav_svn__check_parent_access(const dav_resource *resource, + const svn_repos_authz_access_t required_access) +{ + const char *path = dav_svn__get_parent_path(resource->info->repos_path, + resource->pool); + + return dav_svn__check_access(resource->info->repos->repo_name, + path, + resource->info->r, + required_access); +} + +dav_error * +dav_svn__check_global_access(const dav_resource *resource, + const svn_repos_authz_access_t required_access) +{ + return dav_svn__check_access(resource->info->repos->repo_name, + NULL, /* global access */ + resource->info->r, + required_access); +} diff -ru svn-trunk-r21200/subversion/mod_dav_svn/dav_svn.h svn-r21200-native-authz/subversion/mod_dav_svn/dav_svn.h --- svn-trunk-r21200/subversion/mod_dav_svn/dav_svn.h 2006-08-07 20:48:05.000000000 +0300 +++ svn-r21200-native-authz/subversion/mod_dav_svn/dav_svn.h 2006-08-24 00:50:41.000000000 +0300 @@ -255,6 +255,12 @@ SVNParentPath allowed? */ svn_boolean_t dav_svn__get_list_parentpath_flag(request_rec *r); +/* for the repository referred to by this request, is native authz active? */ +svn_boolean_t dav_svn__get_native_authz_flag(request_rec *r); + +/* for the repository referred to by this request, where is the access + file for native authz */ +const char *dav_svn__get_native_authz_file(request_rec *r); /* SPECIAL URI @@ -677,7 +683,6 @@ int http_status, request_rec *r); - /* Return a writable generic stream that will encode its output to base64 and send it to the Apache filter OUTPUT using BB. Allocate the stream in POOL. */ @@ -686,7 +691,29 @@ ap_filter_t *output, apr_pool_t *pool); +/* Native path-based authorization */ +dav_error * +dav_svn__check_access(const char *repos_name, + const char *repos_path, + request_rec *r, + svn_repos_authz_access_t required_access); + +/* Helpers for path-based authorization */ +dav_error * +dav_svn__check_resource_access(const dav_resource *resource, + const svn_repos_authz_access_t required_access); + +dav_error * +dav_svn__check_parent_access(const dav_resource *resource, + const svn_repos_authz_access_t required_access); + +dav_error * +dav_svn__check_global_access(const dav_resource *resource, + const svn_repos_authz_access_t required_access); +/* Helper to get parent directory path */ +const char *dav_svn__get_parent_path(const char *path, + apr_pool_t *pool); #ifdef __cplusplus } #endif /* __cplusplus */ diff -ru svn-trunk-r21200/subversion/mod_dav_svn/lock.c svn-r21200-native-authz/subversion/mod_dav_svn/lock.c --- svn-trunk-r21200/subversion/mod_dav_svn/lock.c 2006-08-07 20:03:03.000000000 +0300 +++ svn-r21200-native-authz/subversion/mod_dav_svn/lock.c 2006-08-24 00:50:41.000000000 +0300 @@ -630,12 +630,30 @@ svn_error_t *serr; dav_error *derr; - /* If the resource's fs path is unreadable, we don't allow a lock to - be created on it. */ - if (! dav_svn__allow_read(resource, SVN_INVALID_REVNUM, resource->pool)) - return dav_new_error(resource->pool, HTTP_FORBIDDEN, - DAV_ERR_LOCK_SAVE_LOCK, - "Path is not accessible."); + /* This whole if(...) is a hack untill we get rid of mod_authz_svn + * completely. During the times of mod_dav_svn, there was not + * equivalent of dav_svn__allow_read() for writing. + * + * LOCK command needs write access to resource. If native authz + * is turned off, we resort to the old way of requiring read access + * on the resource. + */ + if (dav_svn__get_native_authz_flag(resource->info->r)) + { + /* Path-based authorization: LOCK needs write access to resource */ + derr = dav_svn__check_resource_access(resource, svn_authz_write); + if (derr) + return derr; + } + else + { + /* If the resource's fs path is unreadable, we don't allow a lock to + be created on it. */ + if (! dav_svn__allow_read(resource, SVN_INVALID_REVNUM, resource->pool)) + return dav_new_error(resource->pool, HTTP_FORBIDDEN, + DAV_ERR_LOCK_SAVE_LOCK, + "Path is not accessible."); + } if (lock->next) return dav_new_error(resource->pool, HTTP_BAD_REQUEST, @@ -795,12 +813,24 @@ if (info->keep_locks) return 0; - /* If the resource's fs path is unreadable, we don't allow a lock to - be removed from it. */ - if (! dav_svn__allow_read(resource, SVN_INVALID_REVNUM, resource->pool)) - return dav_new_error(resource->pool, HTTP_FORBIDDEN, - DAV_ERR_LOCK_SAVE_LOCK, - "Path is not accessible."); + if (dav_svn__get_native_authz_flag(resource->info->r)) + { + /* Path-based authorization: UNLOCK needs write access to resource */ + dav_error *derr; + + derr = dav_svn__check_resource_access(resource, svn_authz_write); + if (derr) + return derr; + } + else + { + /* If the resource's fs path is unreadable, we don't allow a lock to + be removed from it. */ + if (! dav_svn__allow_read(resource, SVN_INVALID_REVNUM, resource->pool)) + return dav_new_error(resource->pool, HTTP_FORBIDDEN, + DAV_ERR_LOCK_SAVE_LOCK, + "Path is not accessible."); + } if (locktoken == NULL) { diff -ru svn-trunk-r21200/subversion/mod_dav_svn/mod_dav_svn.c svn-r21200-native-authz/subversion/mod_dav_svn/mod_dav_svn.c --- svn-trunk-r21200/subversion/mod_dav_svn/mod_dav_svn.c 2006-08-07 19:56:48.000000000 +0300 +++ svn-r21200-native-authz/subversion/mod_dav_svn/mod_dav_svn.c 2006-08-24 00:50:41.000000000 +0300 @@ -65,9 +65,10 @@ enum conf_flag autoversioning; /* whether autoversioning is active */ enum conf_flag do_path_authz; /* whether GET subrequests are active */ enum conf_flag list_parentpath; /* whether to allow GET of parentpath */ + enum conf_flag do_native_authz; /* whether native authz is active */ + const char *native_authz_file; /* rule file for native authz */ } dir_conf_t; - #define INHERIT_VALUE(parent, child, field) \ ((child)->field ? (child)->field : (parent)->field) @@ -157,6 +158,9 @@ newconf->autoversioning = INHERIT_VALUE(parent, child, autoversioning); newconf->do_path_authz = INHERIT_VALUE(parent, child, do_path_authz); newconf->list_parentpath = INHERIT_VALUE(parent, child, list_parentpath); + newconf->do_native_authz = INHERIT_VALUE(parent, child, do_native_authz); + newconf->native_authz_file = INHERIT_VALUE(parent, child, + native_authz_file); return newconf; } @@ -211,7 +215,6 @@ return NULL; } - static const char * SVNListParentPath_cmd(cmd_parms *cmd, void *config, int arg) { @@ -225,7 +228,6 @@ return NULL; } - static const char * SVNPath_cmd(cmd_parms *cmd, void *config, const char *arg1) { @@ -240,7 +242,6 @@ return NULL; } - static const char * SVNParentPath_cmd(cmd_parms *cmd, void *config, const char *arg1) { @@ -287,6 +288,29 @@ return NULL; } +static const char * +SVNNativeAuthz_cmd(cmd_parms *cmd, void *config, int arg) +{ + dir_conf_t *conf = config; + + if (arg) + conf->do_native_authz = CONF_FLAG_ON; + else + conf->do_native_authz = CONF_FLAG_OFF; + + return NULL; +} +static const char * +SVNNativeAuthzFile_cmd(cmd_parms *cmd, void *config, const char *arg1) +{ + dir_conf_t *conf = config; + + conf->native_authz_file + = svn_path_canonicalize(apr_pstrdup(cmd->pool, arg1), cmd->pool); + + return NULL; +} + /** Accessor functions for the module's configuration state **/ @@ -415,6 +439,23 @@ return conf->list_parentpath == CONF_FLAG_ON; } +svn_boolean_t +dav_svn__get_native_authz_flag(request_rec *r) +{ + dir_conf_t *conf; + + conf = ap_get_module_config(r->per_dir_config, &dav_svn_module); + return conf->do_native_authz == CONF_FLAG_ON; +} + +const char * +dav_svn__get_native_authz_file(request_rec *r) +{ + dir_conf_t *conf; + + conf = ap_get_module_config(r->per_dir_config, &dav_svn_module); + return conf->native_authz_file; +} static void merge_xml_filter_insert(request_rec *r) @@ -577,6 +618,17 @@ AP_INIT_FLAG("SVNListParentPath", SVNListParentPath_cmd, NULL, ACCESS_CONF|RSRC_CONF, "allow GET of SVNParentPath."), + /* per directory/location */ + AP_INIT_FLAG("SVNNativeAuthz", SVNNativeAuthz_cmd, NULL, + ACCESS_CONF|RSRC_CONF, + "use mod_dav_svn native path-based authorization"), + + /* per directory/location */ + AP_INIT_TAKE1("SVNNativeAuthzFile", SVNNativeAuthzFile_cmd, NULL, + ACCESS_CONF|RSRC_CONF, + "Text file containing permissions of repository paths " + "for mod_dav_svn native path-based authorization"), + { NULL } }; diff -ru svn-trunk-r21200/subversion/mod_dav_svn/repos.c svn-r21200-native-authz/subversion/mod_dav_svn/repos.c --- svn-trunk-r21200/subversion/mod_dav_svn/repos.c 2006-08-07 20:48:05.000000000 +0300 +++ svn-r21200-native-authz/subversion/mod_dav_svn/repos.c 2006-08-24 00:50:41.000000000 +0300 @@ -1485,6 +1485,46 @@ if (err) return err; +#ifdef SVN_DEBUG + ap_log_rerror (APLOG_MARK, APLOG_INFO, 0, r, + "dav_svn_get_resource(): %s %s %s (%s)", + (r->user ? r->user : "-"), + r->method, repos_path, r->uri); +#endif + + /* A special case of path-based authorization for methods + * that don't have any saner place to insert authorization to. + * XXX: Think of how to do it differently! + * + * OPTIONS: + * A general check, if access is allowed to this resouce, + * then OPTIONS will return a valid response. Otherwise + * an "Isufficient rights ..." will be returned. + * + * PROPFIND, PROPPATCH: + * An initial check. If the user is not allowed to access + * the resouce, no information about it should be revealed + * (even "resource does not exist"). Depending on the "Depth" + * header, there will be separate authz checks for every + * child of this resource. + */ + if( r->method_number == M_OPTIONS + || r->method_number == M_PROPFIND + || r->method_number == M_PROPPATCH ) + { + /* NOTE: We cannot use "repos_path" or "relative" straigh away, + * need to add a slash at the beginning... + */ + char* path = NULL; + + if( repos_path ) + path = svn_path_join("/", repos_path, r->pool); + + err = dav_svn__check_access(repos_name, path, r, svn_authz_read); + if (err) + return err; + } + /* The path that we will eventually try to open as an svn repository. Normally defined by the SVNPath directive. */ fs_path = dav_svn__get_fs_path(r); @@ -1747,41 +1787,18 @@ "software."); } - -/* Helper func: return the parent of PATH, allocated in POOL. */ -static const char * -get_parent_path(const char *path, apr_pool_t *pool) -{ - apr_size_t len; - const char *parentpath, *base_name; - char *tmp = apr_pstrdup(pool, path); - - len = strlen(tmp); - - if (len > 0) - { - /* Remove any trailing slash; else svn_path_split() asserts. */ - if (tmp[len-1] == '/') - tmp[len-1] = '\0'; - svn_path_split(tmp, &parentpath, &base_name, pool); - - return parentpath; - } - - return path; -} - - static dav_error * get_parent_resource(const dav_resource *resource, dav_resource **parent_resource) { dav_resource *parent; dav_resource_private *parentinfo; - svn_stringbuf_t *path = resource->info->uri_path; + + svn_stringbuf_t *uri_path = resource->info->uri_path; + const char *repos_path = resource->info->repos_path; /* the root of the repository has no parent */ - if (path->len == 1 && *path->data == '/') + if (uri_path->len == 1 && *uri_path->data == '/') { *parent_resource = NULL; return NULL; @@ -1800,19 +1817,20 @@ parent->versioned = 1; parent->hooks = resource->hooks; parent->pool = resource->pool; - parent->uri = get_parent_path(resource->uri, resource->pool); + parent->uri = dav_svn__get_parent_path(resource->uri, resource->pool); parent->info = parentinfo; parentinfo->pool = resource->info->pool; parentinfo->uri_path = - svn_stringbuf_create(get_parent_path(resource->info->uri_path->data, - resource->pool), resource->pool); + svn_stringbuf_create(dav_svn__get_parent_path(uri_path->data, + resource->pool), + resource->pool); parentinfo->repos = resource->info->repos; parentinfo->root = resource->info->root; parentinfo->r = resource->info->r; parentinfo->svn_client_options = resource->info->svn_client_options; - parentinfo->repos_path = get_parent_path(resource->info->repos_path, - resource->pool); + parentinfo->repos_path = dav_svn__get_parent_path(repos_path, + resource->pool); *parent_resource = parent; break; @@ -2066,6 +2084,11 @@ "Resource body changes may only be made to " "working resources [at this time]."); } + + /* Path-based authorization: PUT requires write access to resource. */ + derr = dav_svn__check_resource_access(resource, svn_authz_write); + if (derr != NULL) + return derr; } #if 1 @@ -2371,13 +2394,24 @@ set_headers(request_rec *r, const dav_resource *resource) { svn_error_t *serr; + dav_error *derr; svn_filesize_t length; const char *mimetype = NULL; apr_time_t last_modified; - + if (!resource->exists) return NULL; + /* Path-based authorization: if the user doesn't have access + * to this resource, no information about it should be revealed. + * + * Here we check for read access, as dav_svn_set_headers() is a + * first step of processing a GET request. + */ + derr = dav_svn__check_resource_access(resource, svn_authz_read); + if (derr) + return derr; + last_modified = get_last_modified(resource); if (last_modified != -1) { @@ -2554,7 +2588,7 @@ return dav_new_error(resource->pool, HTTP_CONFLICT, 0, "Cannot GET this type of resource."); } - + if (resource->collection) { const int gen_html = !resource->info->repos->xslt_uri; @@ -2723,6 +2757,35 @@ const char *href = name; svn_boolean_t is_dir = (entry->kind == svn_node_dir); + /* Path-based authorization: check if the user has read access + * on the entry - if not, hide it. + */ + { + dav_error* derr = NULL; + const char *path = NULL; + const char *repos_path = resource->info->repos_path; + + svn_pool_clear(entry_pool); + + /* Make a repos_path for an entry we are checking - + * If repos_path is "/", we only need to add an entry name. + * If repos_path is "/...", we need to add a slash and + * then the entry name. + */ + path = apr_psprintf(entry_pool, "%s%s%s", + repos_path, + (repos_path[0] == '/' + && repos_path[1] == 0 ? "" : "/"), + name); + + derr = dav_svn__check_access(resource->info->repos->repo_name, + path, + resource->info->r, + svn_authz_read); + if(derr) + continue; + } + svn_pool_clear(entry_pool); /* append a trailing slash onto the name for directories. we NEED @@ -2956,6 +3019,11 @@ "MKCOL called on regular resource, but " "autoversioning is not active."); + /* Path-based authorization: MKCOL requires write access to the resource */ + err = dav_svn__check_resource_access(resource, svn_authz_write); + if (err) + return err; + /* ### note that the parent was checked out at some point, and this ### is being preformed relative to the working rsrc for that parent */ @@ -3033,6 +3101,19 @@ "COPY called on regular resource, but " "autoversioning is not active."); + /* Path-based authorization: COPY requires recursive read access + * to the source resource and write access to the destination resource. + * XXX: recursive write access? + */ + err = dav_svn__check_resource_access(src, + svn_authz_read|svn_authz_recursive); + if (err) + return err; + + err = dav_svn__check_resource_access(dst, svn_authz_write); + if (err) + return err; + /* Auto-versioning copy of regular resource: */ if (dst->type == DAV_RESOURCE_TYPE_REGULAR) { @@ -3110,10 +3191,25 @@ /* Handle activity deletions (early exit). */ if (resource->type == DAV_RESOURCE_TYPE_ACTIVITY) { + /* Path-based authorization: DELETE of an activity requires + * global write access to the repository. + */ + err = dav_svn__check_global_access(resource, svn_authz_write); + if (err) + return err; + return dav_svn__delete_activity(resource->info->repos, resource->info->root.activity_id); } + /* Path-based authorization: DELETE requires recursive write access + * to the resource. + */ + err = dav_svn__check_resource_access(resource, + svn_authz_write|svn_authz_recursive); + if (err) + return err; + /* ### note that the parent was checked out at some point, and this ### is being preformed relative to the working rsrc for that parent */ @@ -3231,6 +3327,19 @@ "MOVE only allowed on two public URIs, and " "autoversioning must be active."); + /* Path-based authorization: MOVE requires recursive write access + * to source resource and write access to destinaton resource. + * XXX: recursive write access? + */ + err = dav_svn__check_resource_access(src, + svn_authz_write|svn_authz_recursive); + if (err) + return err; + + err = dav_svn__check_resource_access(dst, svn_authz_write); + if (err) + return err; + /* Change the dst VCR into a WR, in place. This creates a txn and changes dst->info->root from a rev-root into a txn-root. */ err = dav_svn__checkout(dst, @@ -3299,6 +3408,23 @@ /* Clear the temporary pool. */ svn_pool_clear(ctx->info.pool); + /* Path-based authorization: initial file resource or + * collection resource. Require read access. */ + if (params->walk_type & DAV_WALKTYPE_AUTH) + { + err = dav_svn__check_resource_access(&ctx->res, svn_authz_read); + if (err) + { + /* Apache's mod_dav doesn't have any mechanism to handle + * access rights violation and returning "403 Forbidden" + * status. For now, we just silently skip the entries + * that are not accessible. + * XXX: better way? + */ + return NULL; + } + } + /* The current resource is a collection (possibly here thru recursion) and this is the invocation for the collection. Alternatively, this is the first [and only] entry to do_walk() for a member resource, so @@ -3381,12 +3507,6 @@ apr_hash_this(hi, &key, &klen, &val); dirent = val; - /* authorize access to this resource, if applicable */ - if (params->walk_type & DAV_WALKTYPE_AUTH) - { - /* ### how/what to do? */ - } - /* append this child to our buffers */ svn_stringbuf_appendbytes(ctx->info.uri_path, key, klen); svn_stringbuf_appendbytes(ctx->uri, key, klen); @@ -3398,6 +3518,22 @@ if (dirent->kind == svn_node_file) { + /* Path-based authorization: file resource. Require read access. */ + if (params->walk_type & DAV_WALKTYPE_AUTH) + { + err = dav_svn__check_resource_access(&ctx->res, svn_authz_read); + if (err) + { + /* Apache's mod_dav doesn't have any mechanism to handle + * access rights violation and returning "403 Forbidden" + * status. For now, we just silently skip the entries + * that are not accessible. + * XXX: better way? + */ + return NULL; + } + } + err = (*params->func)(&ctx->wres, DAV_CALLTYPE_MEMBER); if (err != NULL) return err; diff -ru svn-trunk-r21200/subversion/mod_dav_svn/util.c svn-r21200-native-authz/subversion/mod_dav_svn/util.c --- svn-trunk-r21200/subversion/mod_dav_svn/util.c 2006-08-07 20:48:05.000000000 +0300 +++ svn-r21200-native-authz/subversion/mod_dav_svn/util.c 2006-08-24 00:50:41.000000000 +0300 @@ -439,6 +439,28 @@ r->pool); } +const char *dav_svn__get_parent_path(const char *path, + apr_pool_t *pool) +{ + apr_size_t len; + const char *parentpath, *base_name; + char *tmp = apr_pstrdup(pool, path); + + len = strlen(tmp); + + if (len > 0) + { + /* Remove any trailing slash; else svn_path_split() asserts. */ + if (tmp[len-1] == '/') + tmp[len-1] = '\0'; + svn_path_split(tmp, &parentpath, &base_name, pool); + + return parentpath; + } + + return path; +} + struct brigade_write_baton { diff -ru svn-trunk-r21200/subversion/mod_dav_svn/version.c svn-r21200-native-authz/subversion/mod_dav_svn/version.c --- svn-trunk-r21200/subversion/mod_dav_svn/version.c 2006-08-07 20:48:05.000000000 +0300 +++ svn-r21200-native-authz/subversion/mod_dav_svn/version.c 2006-08-24 00:50:41.000000000 +0300 @@ -261,6 +261,13 @@ dav_error *derr; dav_svn__uri_info parse; + /* Path-based authorization: CHECKOUT requires write access + * to the resource. + */ + derr = dav_svn__check_resource_access(resource, svn_authz_write); + if (derr) + return derr; + /* Auto-Versioning Stuff */ if (auto_checkout) { @@ -958,7 +965,15 @@ const apr_xml_doc *doc, ap_filter_t *output) { - int ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); + int ns; + dav_error *err; + + /* Path-based authorization: REPORT requires read access to the resource */ + err = dav_svn__check_resource_access(resource, svn_authz_read); + if (err) + return err; + + ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (doc->root->ns == ns) { @@ -1037,7 +1052,14 @@ "DAV:activity-collection-set property.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); - + + /* Path-based authorization: MKACTIVITY needs global write access + * to the repository. + */ + err = dav_svn__check_global_access(resource, svn_authz_write); + if (err) + return err; + err = dav_svn__create_activity(resource->info->repos, &txn_name, resource->pool); if (err != NULL)