You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by iv...@apache.org on 2013/01/25 10:59:31 UTC
svn commit: r1438407 - in /subversion/trunk: subversion/include/
subversion/libsvn_repos/ subversion/mod_authz_svn/ subversion/svnserve/
subversion/tests/cmdline/ subversion/tests/cmdline/svntest/
subversion/tests/libsvn_repos/ tools/server-side/
Author: ivan
Date: Fri Jan 25 09:59:30 2013
New Revision: 1438407
URL: http://svn.apache.org/viewvc?rev=1438407&view=rev
Log:
Introduce AuthzSVNGroupsFile option to allow Subversion configurations with
groups stored in a separate file.
* subversion/include/svn_config.h
(SVN_CONFIG_OPTION_GROUPS_DB): New define.
* subversion/include/svn_repos.h
(SVN_REPOS__CONF_GROUPS): New define.
(svn_repos_authz_read2): New optional 'groups_path' parameter.
(svn_repos_authz_parse): New optional 'groups_stream' parameter.
* subversion/libsvn_repos/authz.c
(authz_copy_group): Introduce a callback for groups copying.
(authz_copy_groups): Introduce a helper routine to copy groups from a
specified config to the authz structure. Report error if the
destination authz already contains group definitions.
(svn_repos__authz_read): Support the 'groups_path' parameter. If it is set,
parse the corresponding groups config and copy the groups to the
resulting authz structure using authz_copy_groups.
(svn_repos_authz_read2): New optional 'groups_path' parameter.
(svn_repos_authz_read): Support the 'groups_stream' parameter. If it is
set, parse the corresponding groups config stream and copy the groups
to the resulting authz structure.
* subversion/libsvn_repos/deprecated.c
(svn_repos_authz_read): Pass NULL as the 'groups_path' parameter when
calling svn_repos__authz_read.
* subversion/libsvn_repos/repos.h
(svn_repos__authz_read): New optional 'groups_path' parameter.
* subversion/libsvn_repos/repos.с
(create_conf): Explain the purpose of the new groups-db option.
* subversion/mod_authz_svn/mod_authz_svn.c
(authz_svn_config_rec): Add the 'groups_file' config member.
(AuthzSVNGroupsFile_cmd): Introduce a function to canonicalize the groups
file config value.
(authz_svn_cmds): Add the AuthzSVNGroupsFile option.
(get_access_conf): Log the path to the groups file if it is set. Pass the
groups file path to svn_repos_authz_read2.
* subversion/svnserve/serve.c
(canonicalize_access_file): New function. Factored out from
load_authz_config to be reused for the groups file.
(load_authz_config): Retrieve the SVN_CONFIG_OPTION_GROUPS_DB from the
svnserve config. Canonicalize this value if it is present and pass
it to svn_repos_authz_read2 when loading the authz configuration.
* tools/server-side/svnauthz.c
(svnauthz_opt_state): Add the 'groups_file' member.
(svnauthz__cmdline_options_t): Add the svnauthz__groups_file enum member.
(options_table): Add the --groups-file option and short description for it.
(cmd_table): Update the documentation to reflect the added
svnauthz__groups_file option.
(read_file_contents): New function. Factored out from get_authz_from_txn to
be reused for the groups file contents.
(get_authz_from_txn): Use read_file_contents for both authz and groups
files. Pass the resulting contents to svn_repos_authz_parse.
(get_authz): Pass the groups file from options to get_authz_from_txn
or svn_repos_authz_read2 depending on whether transaction option is set.
(canonicalize_access_file): New function. Factored out from
sub_main to be reused for the groups file.
(sub_main): Grab the groups file option from the command line, canonicalize
it if it present and pass it further as a part of the opt_state.
* subversion/tests/libsvn_repos/repos-test.c
(authz-get-handle, in_repo_authz): Pass NULL as the 'groups_path' parameter
when calling svn_repos_authz_read2. Pass NULL as the 'groups_stream'
parameter when calling svn_repos_authz_parse.
(authz_groups_get_handle): Introduce the helper routine for tests similiar
to authz_get_handle but supporting a separate groups file.
(groups_authz, in_repo_groups_authz): Add.
* subversion/tests/cmdline/authz_tests.py
(authz_svnserve_groups): Add the access test for svnserve configured with
a separate groups file.
(test_list): Add a reference to the new test.
* subversion/tests/cmdline/svntest/main.py
(write_restrictive_svnserve_conf_with_groups): New method. Creates a
default restrictive svnserve configuration with a separate groups file.
(write_groups_file): Introduce a helper method to write the groups file
in tests.
* subversion/tests/cmdline/svntest/sandbox.py
(_set_name): Store the default path to the groups file in the
'Sandbox.groups_file' variable.
* subversion/mod_authz_svn/INSTALL
(II.1): Describe usage of AuthzSVNGroupsFile directive.
Patch by: Evgeny Kotkov <evgeny.kotkov{_AT_}visualsvn.com>
Modified:
subversion/trunk/subversion/include/svn_config.h
subversion/trunk/subversion/include/svn_repos.h
subversion/trunk/subversion/libsvn_repos/authz.c
subversion/trunk/subversion/libsvn_repos/deprecated.c
subversion/trunk/subversion/libsvn_repos/repos.c
subversion/trunk/subversion/libsvn_repos/repos.h
subversion/trunk/subversion/mod_authz_svn/INSTALL
subversion/trunk/subversion/mod_authz_svn/mod_authz_svn.c
subversion/trunk/subversion/svnserve/serve.c
subversion/trunk/subversion/tests/cmdline/authz_tests.py
subversion/trunk/subversion/tests/cmdline/svntest/main.py
subversion/trunk/subversion/tests/cmdline/svntest/sandbox.py
subversion/trunk/subversion/tests/libsvn_repos/repos-test.c
subversion/trunk/tools/server-side/svnauthz.c
Modified: subversion/trunk/subversion/include/svn_config.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_config.h?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_config.h (original)
+++ subversion/trunk/subversion/include/svn_config.h Fri Jan 25 09:59:30 2013
@@ -149,6 +149,7 @@ typedef struct svn_config_t svn_config_t
#define SVN_CONFIG_OPTION_USE_SASL "use-sasl"
#define SVN_CONFIG_OPTION_MIN_SSF "min-encryption"
#define SVN_CONFIG_OPTION_MAX_SSF "max-encryption"
+#define SVN_CONFIG_OPTION_GROUPS_DB "groups-db"
/* For repository password database */
#define SVN_CONFIG_SECTION_USERS "users"
Modified: subversion/trunk/subversion/include/svn_repos.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_repos.h?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_repos.h (original)
+++ subversion/trunk/subversion/include/svn_repos.h Fri Jan 25 09:59:30 2013
@@ -3171,19 +3171,24 @@ svn_repos_authz_read(svn_authz_t **authz
* url, an absolute file url, or a registry path) into @a *authz_p,
* allocated in @a pool.
*
- * If @a path is not a valid authz rule file, then return
+ * If @a groups_path (a file, repos relative url, an absolute file url,
+ * or a registry path) is set, use the global groups parsed from it.
+ *
+ * If @a path or @a groups_path is not a valid authz rule file, then return
* #SVN_ERR_AUTHZ_INVALID_CONFIG. The contents of @a *authz_p is then
- * undefined. If @a must_exist is TRUE, a missing authz file is also
- * an error.
+ * undefined. If @a must_exist is TRUE, a missing authz or groups file
+ * is also an error.
*
* If @a path is a repos relative URL then @a repos_root must be set to
* the root of the repository the authz configuration will be used with.
+ * The same applies to @a groups_path if it is being used.
*
* @since New in 1.8
*/
svn_error_t *
svn_repos_authz_read2(svn_authz_t **authz_p,
const char *path,
+ const char *groups_path,
svn_boolean_t must_exist,
const char *repos_root,
apr_pool_t *pool);
@@ -3193,11 +3198,14 @@ svn_repos_authz_read2(svn_authz_t **auth
* Read authz configuration data from @a stream into @a *authz_p,
* allocated in @a pool.
*
+ * If @a groups_stream is set, use the global groups parsed from it.
+ *
* @since New in 1.8
*/
svn_error_t *
svn_repos_authz_parse(svn_authz_t **authz_p,
svn_stream_t *stream,
+ svn_stream_t *groups_stream,
apr_pool_t *pool);
/**
Modified: subversion/trunk/subversion/libsvn_repos/authz.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/authz.c?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/authz.c (original)
+++ subversion/trunk/subversion/libsvn_repos/authz.c Fri Jan 25 09:59:30 2013
@@ -919,20 +919,81 @@ authz_retrieve_config(svn_config_t **cfg
return SVN_NO_ERROR;
}
+
+/* Callback to copy (name, value) group into the "groups" section
+ of another configuration. */
+static svn_boolean_t
+authz_copy_group(const char *name, const char *value,
+ void *baton, apr_pool_t *pool)
+{
+ svn_config_t *authz_cfg = baton;
+
+ svn_config_set(authz_cfg, SVN_CONFIG_SECTION_GROUPS, name, value);
+
+ return TRUE;
+}
+
+/* Copy group definitions from GROUPS_CFG to the resulting AUTHZ.
+ * If AUTHZ already contains any group definition, report an error.
+ * Use POOL for temporary allocations. */
+static svn_error_t *
+authz_copy_groups(svn_authz_t *authz, svn_config_t *groups_cfg,
+ apr_pool_t *pool)
+{
+ /* Easy out: we prohibit local groups in the authz file when global
+ groups are being used. */
+ if (svn_config_has_section(authz->cfg, SVN_CONFIG_SECTION_GROUPS))
+ {
+ return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
+ "Authz file cannot contain any groups "
+ "when global groups are being used.");
+ }
+
+ svn_config_enumerate2(groups_cfg, SVN_CONFIG_SECTION_GROUPS,
+ authz_copy_group, authz->cfg, pool);
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_repos__authz_read(svn_authz_t **authz_p, const char *path,
- svn_boolean_t must_exist, svn_boolean_t accept_urls,
- const char *repos_root, apr_pool_t *pool)
+ const char *groups_path, svn_boolean_t must_exist,
+ svn_boolean_t accept_urls, const char *repos_root,
+ apr_pool_t *pool)
{
svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
- /* Load the rule file */
+ /* Load the authz file */
if (accept_urls)
SVN_ERR(authz_retrieve_config(&authz->cfg, path, must_exist, repos_root,
pool));
else
SVN_ERR(svn_config_read2(&authz->cfg, path, must_exist, TRUE, pool));
+ if (groups_path)
+ {
+ svn_config_t *groups_cfg;
+ svn_error_t *err;
+
+ /* Load the groups file */
+ if (accept_urls)
+ SVN_ERR(authz_retrieve_config(&groups_cfg, groups_path, must_exist,
+ repos_root, pool));
+ else
+ SVN_ERR(svn_config_read2(&groups_cfg, groups_path, must_exist,
+ TRUE, pool));
+
+ /* Copy the groups from groups_cfg into authz. */
+ err = authz_copy_groups(authz, groups_cfg, pool);
+
+ /* Add the paths to the error stack since the authz_copy_groups
+ routine knows nothing about them. */
+ if (err != SVN_NO_ERROR)
+ return svn_error_createf(err->apr_err, err,
+ "Error reading authz file '%s' with "
+ "groups file '%s':", path, groups_path);
+ }
+
/* Make sure there are no errors in the configuration. */
SVN_ERR(authz_validate(authz, pool));
@@ -946,23 +1007,33 @@ svn_repos__authz_read(svn_authz_t **auth
svn_error_t *
svn_repos_authz_read2(svn_authz_t **authz_p, const char *path,
- svn_boolean_t must_exist, const char *repos_root,
- apr_pool_t *pool)
+ const char *groups_path, svn_boolean_t must_exist,
+ const char *repos_root, apr_pool_t *pool)
{
- return svn_repos__authz_read(authz_p, path, must_exist, TRUE, repos_root,
- pool);
+ return svn_repos__authz_read(authz_p, path, groups_path, must_exist,
+ TRUE, repos_root, pool);
}
svn_error_t *
svn_repos_authz_parse(svn_authz_t **authz_p, svn_stream_t *stream,
- apr_pool_t *pool)
+ svn_stream_t *groups_stream, apr_pool_t *pool)
{
svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
- /* Parse the stream */
+ /* Parse the authz stream */
SVN_ERR(svn_config_parse(&authz->cfg, stream, TRUE, pool));
+ if (groups_stream)
+ {
+ svn_config_t *groups_cfg;
+
+ /* Parse the groups stream */
+ SVN_ERR(svn_config_parse(&groups_cfg, groups_stream, TRUE, pool));
+
+ SVN_ERR(authz_copy_groups(authz, groups_cfg, pool));
+ }
+
/* Make sure there are no errors in the configuration. */
SVN_ERR(authz_validate(authz, pool));
Modified: subversion/trunk/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/deprecated.c?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/trunk/subversion/libsvn_repos/deprecated.c Fri Jan 25 09:59:30 2013
@@ -1013,5 +1013,6 @@ svn_error_t *
svn_repos_authz_read(svn_authz_t **authz_p, const char *file,
svn_boolean_t must_exist, apr_pool_t *pool)
{
- return svn_repos__authz_read(authz_p, file, must_exist, FALSE, NULL, pool);
+ return svn_repos__authz_read(authz_p, file, NULL, must_exist,
+ FALSE, NULL, pool);
}
Modified: subversion/trunk/subversion/libsvn_repos/repos.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/repos.c?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/repos.c (original)
+++ subversion/trunk/subversion/libsvn_repos/repos.c Fri Jan 25 09:59:30 2013
@@ -1025,6 +1025,12 @@ create_conf(svn_repos_t *repos, apr_pool
"### no path-based access control is done." NL
"### Uncomment the line below to use the default authorization file." NL
"# authz-db = " SVN_REPOS__CONF_AUTHZ NL
+"### The groups-db option controls the location of the groups file." NL
+"### Unless you specify a path starting with a /, the file's location is" NL
+"### relative to the directory containing this file. The specified path" NL
+"### may be a repository relative URL (^/) or an absolute file:// URL to a" NL
+"### text file in a Subversion repository." NL
+"# groups-db = " SVN_REPOS__CONF_GROUPS NL
"### This option specifies the authentication realm of the repository." NL
"### If two repositories have the same authentication realm, they should" NL
"### have the same password database, and vice versa. The default realm" NL
Modified: subversion/trunk/subversion/libsvn_repos/repos.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/repos.h?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/repos.h (original)
+++ subversion/trunk/subversion/libsvn_repos/repos.h Fri Jan 25 09:59:30 2013
@@ -95,10 +95,11 @@ extern "C" {
#define SVN_REPOS__CONF_SVNSERVE_CONF "svnserve.conf"
/* In the svnserve default configuration, these are the suggested
- locations for the passwd and authz files (in the repository conf
- directory), and we put example templates there. */
+ locations for the passwd, authz and groups files (in the repository
+ conf directory), and we put example templates there. */
#define SVN_REPOS__CONF_PASSWD "passwd"
#define SVN_REPOS__CONF_AUTHZ "authz"
+#define SVN_REPOS__CONF_GROUPS "groups"
/* The Repository object, created by svn_repos_open2() and
svn_repos_create(). */
@@ -307,22 +308,24 @@ svn_repos__hooks_post_unlock(svn_repos_t
/*** Authz Functions ***/
/* Read authz configuration data from PATH into *AUTHZ_P, allocated
- in POOL.
-
- PATH may be a file or a registry path and iff ACCEPT_URLS is set
- it may also be a repos relative url or an absolute file url. When
+ in POOL. If GROUPS_PATH is set, use the global groups parsed from it.
+
+ PATH and GROUPS_PATH may be a file or a registry path and iff ACCEPT_URLS
+ is set it may also be a repos relative url or an absolute file url. When
ACCEPT_URLS is FALSE REPOS_ROOT can be NULL.
-
- If PATH is not a valid authz rule file, then return
+
+ If PATH or GROUPS_PATH is not a valid authz rule file, then return
SVN_AUTHZ_INVALID_CONFIG. The contents of *AUTHZ_P is then
- undefined. If MUST_EXIST is TRUE, a missing authz file is also
- an error.
-
+ undefined. If MUST_EXIST is TRUE, a missing authz or global groups file
+ is also an error.
+
If PATH is a repos relative URL then REPOS_ROOT must be set to
- the root of the repository the authz configuration will be used with. */
+ the root of the repository the authz configuration will be used with.
+ The same applies to GROUPS_PATH if it is being used. */
svn_error_t *
svn_repos__authz_read(svn_authz_t **authz_p,
const char *path,
+ const char *groups_path,
svn_boolean_t must_exist,
svn_boolean_t accept_urls,
const char *repos_root,
Modified: subversion/trunk/subversion/mod_authz_svn/INSTALL
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_authz_svn/INSTALL?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_authz_svn/INSTALL (original)
+++ subversion/trunk/subversion/mod_authz_svn/INSTALL Fri Jan 25 09:59:30 2013
@@ -186,6 +186,24 @@ II. Configuration
The "Require" statement in the previous example is not strictly
needed, but has been included for clarity.
+ H. Example 8: Separate authz and groups files.
+
+ This configuration allows storing the groups separately from the
+ main authz file with the authorization rules.
+
+ <Location /svn>
+ DAV svn
+ SVNParentPath /path/to/reposparent
+
+ AuthType Basic
+ AuthName "Subversion repository"
+ AuthUserFile /path/to/htpasswd/file
+
+ AuthzSVNAccessFile /path/to/access/file
+ AuthzSVNGroupsFile /path/to/groups/file
+
+ Require valid-user
+ </Location>
2. Specifying permissions
Modified: subversion/trunk/subversion/mod_authz_svn/mod_authz_svn.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_authz_svn/mod_authz_svn.c?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_authz_svn/mod_authz_svn.c (original)
+++ subversion/trunk/subversion/mod_authz_svn/mod_authz_svn.c Fri Jan 25 09:59:30 2013
@@ -63,6 +63,7 @@ typedef struct authz_svn_config_rec {
const char *base_path;
const char *access_file;
const char *repo_relative_access_file;
+ const char *groups_file;
const char *force_username_case;
} authz_svn_config_rec;
@@ -147,6 +148,16 @@ AuthzSVNReposRelativeAccessFile_cmd(cmd_
return NULL;
}
+static const char *
+AuthzSVNGroupsFile_cmd(cmd_parms *cmd, void *config, const char *arg1)
+{
+ authz_svn_config_rec *conf = config;
+
+ conf->groups_file = canonicalize_access_file(arg1, TRUE, cmd->pool);
+
+ return NULL;
+}
+
/* Implements the #cmds member of Apache's #module vtable. */
static const command_rec authz_svn_cmds[] =
{
@@ -170,6 +181,14 @@ static const command_rec authz_svn_cmds[
"file containing permissions of repository paths. Path may "
"be an repository relative URL (^/) or absolute file:// URL "
"to a text file in a Subversion repository."),
+ AP_INIT_TAKE1("AuthzSVNGroupsFile",
+ AuthzSVNGroupsFile_cmd,
+ NULL,
+ OR_AUTHCFG,
+ "Path to text file containing group definitions for all "
+ "repositories. Path may be an repository relative URL (^/) "
+ "or absolute file:// URL to a text file in a Subversion "
+ "repository."),
AP_INIT_FLAG("AuthzSVNAnonymous", ap_set_flag_slot,
(void *)APR_OFFSETOF(authz_svn_config_rec, anonymous),
OR_AUTHCFG,
@@ -331,6 +350,12 @@ get_access_conf(request_rec *r, authz_sv
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"Path to authz file is %s", access_file);
+ if (conf->groups_file)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Path to groups file is %s", conf->groups_file);
+ }
+
cache_key = apr_pstrcat(scratch_pool, "mod_authz_svn:",
access_file, (char *)NULL);
apr_pool_userdata_get(&user_data, cache_key, r->connection->pool);
@@ -338,7 +363,8 @@ get_access_conf(request_rec *r, authz_sv
if (access_conf == NULL)
{
svn_err = svn_repos_authz_read2(&access_conf, access_file,
- TRUE, repos_path, r->connection->pool);
+ conf->groups_file, TRUE, repos_path,
+ r->connection->pool);
if (svn_err)
{
log_svn_error(APLOG_MARK, r,
Modified: subversion/trunk/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnserve/serve.c?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/svnserve/serve.c (original)
+++ subversion/trunk/subversion/svnserve/serve.c Fri Jan 25 09:59:30 2013
@@ -269,35 +269,61 @@ svn_error_t *load_pwdb_config(server_bat
return SVN_NO_ERROR;
}
+/* Canonicalize ACCESS_FILE based on the type of argument.
+ * SERVER baton is used to convert relative paths to absolute paths
+ * rooted at the server root. */
+static const char *
+canonicalize_access_file(const char *access_file,
+ server_baton_t *server,
+ apr_pool_t *pool)
+{
+ if (svn_path_is_url(access_file))
+ {
+ access_file = svn_uri_canonicalize(access_file, pool);
+ }
+ else if (!svn_path_is_repos_relative_url(access_file))
+ {
+ access_file = svn_dirent_internal_style(access_file, pool);
+ access_file = svn_dirent_join(server->base, access_file, pool);
+ }
+
+ /* We don't canonicalize repos relative urls since they get
+ * canonicalized inside svn_repos_authz_read2() when they
+ * are resolved. */
+
+ return access_file;
+}
+
svn_error_t *load_authz_config(server_baton_t *server,
svn_ra_svn_conn_t *conn,
const char *repos_root,
apr_pool_t *pool)
{
const char *authzdb_path;
+ const char *groupsdb_path;
svn_error_t *err;
/* Read authz configuration. */
svn_config_get(server->cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
+
+ svn_config_get(server->cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL,
+ SVN_CONFIG_OPTION_GROUPS_DB, NULL);
+
if (authzdb_path)
{
const char *case_force_val;
- /* Canonicalize and add the base onto the authzdb_path (if needed).
- * We don't canonicalize repos relative urls since they are
- * canonicalized when they are resolved in svn_repos_authz_read2(). */
- if (svn_path_is_url(authzdb_path))
- {
- authzdb_path = svn_uri_canonicalize(authzdb_path, pool);
- }
- else if (!svn_path_is_repos_relative_url(authzdb_path))
- {
- authzdb_path = svn_dirent_internal_style(authzdb_path, pool);
- authzdb_path = svn_dirent_join(server->base, authzdb_path, pool);
- }
- err = svn_repos_authz_read2(&server->authzdb, authzdb_path, TRUE,
- repos_root, pool);
+ /* Canonicalize and add the base onto the authzdb_path (if needed). */
+ authzdb_path = canonicalize_access_file(authzdb_path, server, pool);
+
+ /* Same for the groupsdb_path if it is present. */
+ if (groupsdb_path)
+ groupsdb_path = canonicalize_access_file(groupsdb_path,
+ server, pool);
+
+ err = svn_repos_authz_read2(&server->authzdb, authzdb_path,
+ groupsdb_path, TRUE, repos_root, pool);
if (err)
{
log_server_error(err, server, conn, pool);
Modified: subversion/trunk/subversion/tests/cmdline/authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/authz_tests.py?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/authz_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/authz_tests.py Fri Jan 25 09:59:30 2013
@@ -1452,6 +1452,69 @@ def remove_subdir_with_authz_and_tc(sbox
None, None, False,
wc_dir)
+@SkipUnless(svntest.main.is_ra_type_svn)
+def authz_svnserve_groups(sbox):
+ "authz with configured global groups"
+
+ sbox.build(create_wc = False)
+
+ svntest.main.write_restrictive_svnserve_conf_with_groups(sbox.repo_dir)
+
+ svntest.main.write_authz_file(sbox, { "/A/B" : "@senate = r",
+ "/A/D" : "@senate = rw",
+ "/A/B/E" : "@senate = " })
+
+ svntest.main.write_groups_file(sbox, { "senate" : "jrandom" })
+
+ root_url = sbox.repo_url
+ A_url = root_url + '/A'
+ B_url = A_url + '/B'
+ E_url = B_url + '/E'
+ F_url = B_url + '/F'
+ D_url = A_url + '/D'
+ G_url = D_url + '/G'
+ lambda_url = B_url + '/lambda'
+ pi_url = G_url + '/pi'
+ alpha_url = E_url + '/alpha'
+
+ expected_err = ".*svn: E170001: Authorization failed.*"
+
+ # read a remote file
+ svntest.actions.run_and_verify_svn(None, ["This is the file 'lambda'.\n"],
+ [], 'cat',
+ lambda_url)
+
+ # read a remote file
+ svntest.actions.run_and_verify_svn(None, ["This is the file 'pi'.\n"],
+ [], 'cat',
+ pi_url)
+
+ # read a remote file, unreadable: should fail
+ svntest.actions.run_and_verify_svn(None,
+ None, expected_err,
+ 'cat',
+ alpha_url)
+
+ # copy a remote file, source is unreadable: should fail
+ svntest.actions.run_and_verify_svn(None,
+ None, expected_err,
+ 'cp',
+ '-m', 'logmsg',
+ alpha_url, B_url)
+
+ # copy a remote folder
+ svntest.actions.run_and_verify_svn(None, None, [],
+ 'cp',
+ '-m', 'logmsg',
+ F_url, D_url)
+
+ # copy a remote folder, source is unreadable: should fail
+ svntest.actions.run_and_verify_svn(None,
+ None, expected_err,
+ 'cp',
+ '-m', 'logmsg',
+ E_url, D_url)
+
########################################################################
# Run the tests
@@ -1481,7 +1544,8 @@ test_list = [ None,
wc_delete,
wc_commit_error_handling,
upgrade_absent,
- remove_subdir_with_authz_and_tc
+ remove_subdir_with_authz_and_tc,
+ authz_svnserve_groups
]
serial_only = True
Modified: subversion/trunk/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/svntest/main.py?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/trunk/subversion/tests/cmdline/svntest/main.py Fri Jan 25 09:59:30 2013
@@ -1065,6 +1065,19 @@ def write_restrictive_svnserve_conf(repo
fp.write("password-db = passwd\n")
fp.close()
+def write_restrictive_svnserve_conf_with_groups(repo_dir,
+ anon_access="none"):
+ "Create a restrictive configuration with groups stored in a separate file."
+
+ fp = open(get_svnserve_conf_file_path(repo_dir), 'w')
+ fp.write("[general]\nanon-access = %s\nauth-access = write\n"
+ "authz-db = authz\ngroups-db = groups\n" % anon_access)
+ if options.enable_sasl:
+ fp.write("realm = svntest\n[sasl]\nuse-sasl = true\n");
+ else:
+ fp.write("password-db = passwd\n")
+ fp.close()
+
# Warning: because mod_dav_svn uses one shared authz file for all
# repositories, you *cannot* use write_authz_file in any test that
# might be run in parallel.
@@ -1100,6 +1113,18 @@ an appropriate list of mappings.
fp.write("[%s%s]\n%s\n" % (prefix, p, r))
fp.close()
+# See the warning about parallel test execution in write_authz_file
+# method description.
+def write_groups_file(sbox, groups):
+ """Write a groups file to SBOX, appropriate for the RA method used,
+with group contents set to GROUPS."""
+ fp = open(sbox.groups_file, 'w')
+ fp.write("[groups]\n")
+ if groups:
+ for p, r in groups.items():
+ fp.write("%s = %s\n" % (p, r))
+ fp.close()
+
def use_editor(func):
os.environ['SVN_EDITOR'] = svneditor_script
os.environ['SVN_MERGE'] = svneditor_script
Modified: subversion/trunk/subversion/tests/cmdline/svntest/sandbox.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/svntest/sandbox.py?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/svntest/sandbox.py (original)
+++ subversion/trunk/subversion/tests/cmdline/svntest/sandbox.py Fri Jan 25 09:59:30 2013
@@ -78,12 +78,14 @@ class Sandbox:
tmp_authz_file = os.path.join(svntest.main.work_dir, "authz-" + self.name)
open(tmp_authz_file, 'w').write("[/]\n* = rw\n")
shutil.move(tmp_authz_file, self.authz_file)
+ self.groups_file = os.path.join(svntest.main.work_dir, "groups")
# For svnserve tests we have a per-repository authz file, and it
# doesn't need to be there in order for things to work, so we don't
# have any default contents.
elif self.repo_url.startswith("svn"):
self.authz_file = os.path.join(self.repo_dir, "conf", "authz")
+ self.groups_file = os.path.join(self.repo_dir, "conf", "groups")
def clone_dependent(self, copy_wc=False):
"""A convenience method for creating a near-duplicate of this
Modified: subversion/trunk/subversion/tests/libsvn_repos/repos-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_repos/repos-test.c?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_repos/repos-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_repos/repos-test.c Fri Jan 25 09:59:30 2013
@@ -1157,7 +1157,7 @@ authz_get_handle(svn_authz_t **authz_p,
SVN_ERR_W(svn_stream_puts(stream, authz_contents),
"Writing authz contents to stream");
- SVN_ERR_W(svn_repos_authz_parse(authz_p, stream, pool),
+ SVN_ERR_W(svn_repos_authz_parse(authz_p, stream, NULL, pool),
"Parsing the authz contents");
SVN_ERR_W(svn_stream_close(stream),
@@ -1451,24 +1451,25 @@ in_repo_authz(const svn_test_opts_t *opt
/* repos relative URL */
repos_root = svn_repos_path(repos, pool);
- SVN_ERR(svn_repos_authz_read2(&authz_cfg, "^/authz", TRUE, repos_root,
- pool));
+ SVN_ERR(svn_repos_authz_read2(&authz_cfg, "^/authz", NULL, TRUE,
+ repos_root, pool));
SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
/* absolute file URL, repos_root is NULL to validate the contract that it
* is not needed except when a repos relative URL is passed. */
SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_url, repos_root, pool));
authz_url = apr_pstrcat(pool, repos_url, "/authz", (char *)NULL);
- SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, TRUE, NULL, pool));
+ SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, NULL, TRUE,
+ NULL, pool));
SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
/* Non-existant path in the repo with must_exist set to FALSE */
- SVN_ERR(svn_repos_authz_read2(&authz_cfg, "^/A/authz", FALSE, repos_root,
- pool));
+ SVN_ERR(svn_repos_authz_read2(&authz_cfg, "^/A/authz", NULL, FALSE,
+ repos_root, pool));
/* Non-existant path in the repo with must_exist set to TRUE */
- err = svn_repos_authz_read2(&authz_cfg, "^/A/authz", TRUE, repos_root,
- pool);
+ err = svn_repos_authz_read2(&authz_cfg, "^/A/authz", NULL, TRUE,
+ repos_root, pool);
if (!err || err->apr_err != SVN_ERR_AUTHZ_INVALID_CONFIG)
return svn_error_createf(SVN_ERR_TEST_FAILED, err,
"Got %s error instead of expected "
@@ -1478,7 +1479,7 @@ in_repo_authz(const svn_test_opts_t *opt
/* http:// URL which is unsupported */
err = svn_repos_authz_read2(&authz_cfg, "http://example.com/repo/authz",
- TRUE, repos_root, pool);
+ NULL, TRUE, repos_root, pool);
if (!err || err->apr_err != SVN_ERR_RA_ILLEGAL_URL)
return svn_error_createf(SVN_ERR_TEST_FAILED, err,
"Got %s error instead of expected "
@@ -1488,6 +1489,149 @@ in_repo_authz(const svn_test_opts_t *opt
/* svn:// URL which is unsupported */
err = svn_repos_authz_read2(&authz_cfg, "svn://example.com/repo/authz",
+ NULL, TRUE, repos_root, pool);
+ if (!err || err->apr_err != SVN_ERR_RA_ILLEGAL_URL)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, err,
+ "Got %s error instead of expected "
+ "SVN_ERR_RA_ILLEGAL_URL",
+ err ? "unexpected" : "no");
+ svn_error_clear(err);
+
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Test in-repo authz with global groups. */
+static svn_error_t *
+in_repo_groups_authz(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_repos_t *repos;
+ svn_fs_t *fs;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ svn_revnum_t youngest_rev;
+ svn_authz_t *authz_cfg;
+ const char *groups_contents;
+ const char *authz_contents;
+ const char *repos_root;
+ const char *repos_url;
+ const char *groups_url;
+ const char *authz_url;
+ svn_error_t *err;
+ struct check_access_tests test_set[] = {
+ /* reads */
+ { "/A", NULL, NULL, svn_authz_read, FALSE },
+ { "/A", NULL, "plato", svn_authz_read, TRUE },
+ { "/A", NULL, "socrates", svn_authz_read, TRUE },
+ { "/A", NULL, "solon", svn_authz_read, TRUE },
+ { "/A", NULL, "ephialtes", svn_authz_read, TRUE },
+ /* writes */
+ { "/A", NULL, NULL, svn_authz_write, FALSE },
+ { "/A", NULL, "plato", svn_authz_write, FALSE },
+ { "/A", NULL, "socrates", svn_authz_write, FALSE },
+ { "/A", NULL, "solon", svn_authz_write, TRUE },
+ { "/A", NULL, "ephialtes", svn_authz_write, TRUE },
+ /* Sentinel */
+ { NULL, NULL, NULL, svn_authz_none, FALSE }
+ };
+
+ /* Test plan:
+ * 1. Create an authz file, a global groups file and an empty authz file,
+ * put all these files in the repository. The empty authz file is
+ * required to perform the non-existent path checks (4-7) --
+ * otherwise we would get the authz validation error due to undefined
+ * groups.
+ * 2. Verify that the groups file can be read with an relative URL.
+ * 3. Verify that the groups file can be read with an absolute URL.
+ * 4. Verify that non-existent groups file path does not error out when
+ * must_exist is FALSE.
+ * 5. Same as (4), but when both authz and groups file paths do
+ * not exist.
+ * 6. Verify that non-existent path for the groups file does error out when
+ * must_exist is TRUE.
+ * 7. Verify that an http:// URL produces an error.
+ * 8. Verify that an svn:// URL produces an error.
+ */
+
+ /* What we'll put in the authz and groups files, it's simple since
+ * we're not testing the parsing, just that we got what we expected. */
+
+ groups_contents =
+ "[groups]" NL
+ "philosophers = plato, socrates" NL
+ "senate = solon, ephialtes" NL
+ "" NL;
+
+ authz_contents =
+ "[/]" NL
+ "@senate = rw" NL
+ "@philosophers = r" NL
+ "" NL;
+
+ /* Create a filesystem and repository. */
+ SVN_ERR(svn_test__create_repos(&repos,
+ "test-repo-in-repo-global-groups-authz",
+ opts, pool));
+ fs = svn_repos_fs(repos);
+
+ /* Commit the authz, empty authz and groups files to the repo. */
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+ SVN_ERR(svn_fs_make_file(txn_root, "groups", pool));
+ SVN_ERR(svn_fs_make_file(txn_root, "authz", pool));
+ SVN_ERR(svn_fs_make_file(txn_root, "empty-authz", pool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "groups",
+ groups_contents, pool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "authz",
+ authz_contents, pool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "empty-authz", "", pool));
+ SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool));
+ SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
+
+ /* repos relative URLs */
+ repos_root = svn_repos_path(repos, pool);
+ SVN_ERR(svn_repos_authz_read2(&authz_cfg, "^/authz", "^/groups",
+ TRUE, repos_root, pool));
+ SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
+
+ /* absolute file URLs, repos_root is NULL to validate the contract that it
+ * is not needed except when a repos relative URLs are passed. */
+ SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_url, repos_root, pool));
+ authz_url = apr_pstrcat(pool, repos_url, "/authz", (char *)NULL);
+ groups_url = apr_pstrcat(pool, repos_url, "/groups", (char *)NULL);
+ SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, groups_url,
+ TRUE, NULL, pool));
+ SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
+
+ /* Non-existent path for the groups file with must_exist
+ * set to TRUE */
+ SVN_ERR(svn_repos_authz_read2(&authz_cfg, "^/empty-authz",
+ "^/A/groups", FALSE,
+ repos_root, pool));
+
+ /* Non-existent paths for both the authz and the groups files
+ * with must_exist set to TRUE */
+ SVN_ERR(svn_repos_authz_read2(&authz_cfg, "^/A/authz",
+ "^/A/groups", FALSE,
+ repos_root, pool));
+
+ /* Non-existent path for the groups file with must_exist
+ * set to TRUE */
+ err = svn_repos_authz_read2(&authz_cfg, "^/empty-authz",
+ "^/A/groups", TRUE,
+ repos_root, pool);
+ if (!err || err->apr_err != SVN_ERR_AUTHZ_INVALID_CONFIG)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, err,
+ "Got %s error instead of expected "
+ "SVN_ERR_AUTHZ_INVALID_CONFIG",
+ err ? "unexpected" : "no");
+ svn_error_clear(err);
+
+ /* http:// URL which is unsupported */
+ err = svn_repos_authz_read2(&authz_cfg, "^/empty-authz",
+ "http://example.com/repo/groups",
TRUE, repos_root, pool);
if (!err || err->apr_err != SVN_ERR_RA_ILLEGAL_URL)
return svn_error_createf(SVN_ERR_TEST_FAILED, err,
@@ -1496,10 +1640,264 @@ in_repo_authz(const svn_test_opts_t *opt
err ? "unexpected" : "no");
svn_error_clear(err);
+ /* svn:// URL which is unsupported */
+ err = svn_repos_authz_read2(&authz_cfg, "^/empty-authz",
+ "http://example.com/repo/groups",
+ TRUE, repos_root, pool);
+ if (!err || err->apr_err != SVN_ERR_RA_ILLEGAL_URL)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, err,
+ "Got %s error instead of expected "
+ "SVN_ERR_RA_ILLEGAL_URL",
+ err ? "unexpected" : "no");
+ svn_error_clear(err);
+
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Helper for the groups_authz test. Set *AUTHZ_P to a representation of
+ AUTHZ_CONTENTS in conjuction with GROUPS_CONTENTS, using POOL for
+ temporary allocation. If DISK is TRUE then write the contents to
+ temporary files and use svn_repos_authz_read2() to get the data if FALSE
+ write the data to a buffered stream and use svn_repos_authz_parse(). */
+static svn_error_t *
+authz_groups_get_handle(svn_authz_t **authz_p,
+ const char *authz_contents,
+ const char *groups_contents,
+ svn_boolean_t disk,
+ apr_pool_t *pool)
+{
+ if (disk)
+ {
+ const char *authz_file_path;
+ const char *groups_file_path;
+
+ /* Create temporary files. */
+ SVN_ERR_W(svn_io_write_unique(&authz_file_path, NULL,
+ authz_contents,
+ strlen(authz_contents),
+ svn_io_file_del_on_pool_cleanup, pool),
+ "Writing temporary authz file");
+ SVN_ERR_W(svn_io_write_unique(&groups_file_path, NULL,
+ groups_contents,
+ strlen(groups_contents),
+ svn_io_file_del_on_pool_cleanup, pool),
+ "Writing temporary groups file");
+
+ /* Read the authz configuration back and start testing. */
+ SVN_ERR_W(svn_repos_authz_read2(authz_p, authz_file_path,
+ groups_file_path, TRUE, NULL, pool),
+ "Opening test authz and groups files");
+
+ /* Done with the files. */
+ SVN_ERR_W(svn_io_remove_file(authz_file_path, pool),
+ "Removing test authz file");
+ SVN_ERR_W(svn_io_remove_file(groups_file_path, pool),
+ "Removing test groups file");
+ }
+ else
+ {
+ svn_stream_t *stream;
+ svn_stream_t *groups_stream;
+
+ /* Create the streams. */
+ stream = svn_stream_buffered(pool);
+ groups_stream = svn_stream_buffered(pool);
+
+ SVN_ERR_W(svn_stream_puts(stream, authz_contents),
+ "Writing authz contents to stream");
+ SVN_ERR_W(svn_stream_puts(groups_stream, groups_contents),
+ "Writing groups contents to stream");
+
+ /* Read the authz configuration from the streams and start testing. */
+ SVN_ERR_W(svn_repos_authz_parse(authz_p, stream, groups_stream, pool),
+ "Parsing the authz and groups contents");
+
+ /* Done with the streams. */
+ SVN_ERR_W(svn_stream_close(stream),
+ "Closing the authz stream");
+ SVN_ERR_W(svn_stream_close(groups_stream),
+ "Closing the groups stream");
+ }
return SVN_NO_ERROR;
}
+/* Test authz with global groups. */
+static svn_error_t *
+groups_authz(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_authz_t *authz_cfg;
+ const char *authz_contents;
+ const char *groups_contents;
+ svn_error_t *err;
+
+ struct check_access_tests test_set1[] = {
+ /* reads */
+ { "/A", "greek", NULL, svn_authz_read, FALSE },
+ { "/A", "greek", "plato", svn_authz_read, TRUE },
+ { "/A", "greek", "demetrius", svn_authz_read, TRUE },
+ { "/A", "greek", "galenos", svn_authz_read, TRUE },
+ { "/A", "greek", "pamphilos", svn_authz_read, FALSE },
+ /* writes */
+ { "/A", "greek", NULL, svn_authz_write, FALSE },
+ { "/A", "greek", "plato", svn_authz_write, TRUE },
+ { "/A", "greek", "demetrius", svn_authz_write, FALSE },
+ { "/A", "greek", "galenos", svn_authz_write, FALSE },
+ { "/A", "greek", "pamphilos", svn_authz_write, FALSE },
+ /* Sentinel */
+ { NULL, NULL, NULL, svn_authz_none, FALSE }
+ };
+
+ struct check_access_tests test_set2[] = {
+ /* reads */
+ { "/A", "greek", NULL, svn_authz_read, FALSE },
+ { "/A", "greek", "socrates", svn_authz_read, FALSE },
+ { "/B", "greek", NULL, svn_authz_read, FALSE},
+ { "/B", "greek", "socrates", svn_authz_read, TRUE },
+ /* writes */
+ { "/A", "greek", NULL, svn_authz_write, FALSE },
+ { "/A", "greek", "socrates", svn_authz_write, FALSE },
+ { "/B", "greek", NULL, svn_authz_write, FALSE},
+ { "/B", "greek", "socrates", svn_authz_write, TRUE },
+ /* Sentinel */
+ { NULL, NULL, NULL, svn_authz_none, FALSE }
+ };
+
+ /* Test plan:
+ * 1. Ensure that a simple setup with global groups and access rights in
+ * two separate files works as expected.
+ * 2. Verify that access rights written in the global groups file are
+ * discarded and affect nothing in authorization terms.
+ * 3. Verify that local groups in the authz file are prohibited in
+ * conjuction with global groups (and that a configuration error is
+ * reported in this scenario).
+ * 4. Ensure that group cycles in the global groups file are reported.
+ *
+ * All checks are performed twice -- for the configurations stored on disk
+ * and in memory. See authz_groups_get_handle.
+ */
+
+ groups_contents =
+ "[groups]" NL
+ "slaves = pamphilos,@gladiators" NL
+ "gladiators = demetrius,galenos" NL
+ "philosophers = plato" NL
+ "" NL;
+
+ authz_contents =
+ "[greek:/A]" NL
+ "@slaves = " NL
+ "@gladiators = r" NL
+ "@philosophers = rw" NL
+ "" NL;
+
+ SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, TRUE, pool));
+
+ SVN_ERR(authz_check_access(authz_cfg, test_set1, pool));
+
+ SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, FALSE, pool));
+
+ SVN_ERR(authz_check_access(authz_cfg, test_set1, pool));
+
+ /* Access rights in the global groups file are discarded. */
+ groups_contents =
+ "[groups]" NL
+ "philosophers = socrates" NL
+ "" NL
+ "[greek:/A]" NL
+ "@philosophers = rw" NL
+ "" NL;
+
+ authz_contents =
+ "[greek:/B]" NL
+ "@philosophers = rw" NL
+ "" NL;
+
+ SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, TRUE, pool));
+
+ SVN_ERR(authz_check_access(authz_cfg, test_set2, pool));
+
+ SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, FALSE, pool));
+
+ SVN_ERR(authz_check_access(authz_cfg, test_set2, pool));
+
+ /* Local groups cannot be used in conjuction with global groups. */
+ groups_contents =
+ "[groups]" NL
+ "slaves = maximus" NL
+ "" NL;
+
+ authz_contents =
+ "[greek:/A]" NL
+ "@slaves = " NL
+ "@kings = rw" NL
+ "" NL
+ "[groups]" NL
+ /* That's an epic story of the slave who tried to become a king. */
+ "kings = maximus" NL
+ "" NL;
+
+ err = authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, TRUE, pool);
+
+ if (!err || err->apr_err != SVN_ERR_AUTHZ_INVALID_CONFIG)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, err,
+ "Got %s error instead of expected "
+ "SVN_ERR_AUTHZ_INVALID_CONFIG",
+ err ? "unexpected" : "no");
+ svn_error_clear(err);
+
+ err = authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, FALSE, pool);
+
+ if (!err || err->apr_err != SVN_ERR_AUTHZ_INVALID_CONFIG)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, err,
+ "Got %s error instead of expected "
+ "SVN_ERR_AUTHZ_INVALID_CONFIG",
+ err ? "unexpected" : "no");
+ svn_error_clear(err);
+
+ /* Ensure that group cycles are reported. */
+ groups_contents =
+ "[groups]" NL
+ "slaves = cooks,scribes,@gladiators" NL
+ "gladiators = equites,thraces,@slaves" NL
+ "" NL;
+
+ authz_contents =
+ "[greek:/A]" NL
+ "@slaves = r" NL
+ "" NL;
+
+ err = authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, TRUE, pool);
+
+ if (!err || err->apr_err != SVN_ERR_AUTHZ_INVALID_CONFIG)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, err,
+ "Got %s error instead of expected "
+ "SVN_ERR_AUTHZ_INVALID_CONFIG",
+ err ? "unexpected" : "no");
+ svn_error_clear(err);
+
+ err = authz_groups_get_handle(&authz_cfg, authz_contents,
+ groups_contents, FALSE, pool);
+
+ if (!err || err->apr_err != SVN_ERR_AUTHZ_INVALID_CONFIG)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, err,
+ "Got %s error instead of expected "
+ "SVN_ERR_AUTHZ_INVALID_CONFIG",
+ err ? "unexpected" : "no");
+ svn_error_clear(err);
+
+ return SVN_NO_ERROR;
+}
/* Callback for the commit editor tests that relays requests to
authz. */
@@ -2775,6 +3173,10 @@ struct svn_test_descriptor_t test_funcs[
"test authz access control"),
SVN_TEST_OPTS_PASS(in_repo_authz,
"test authz stored in the repo"),
+ SVN_TEST_OPTS_PASS(in_repo_groups_authz,
+ "test authz and global groups stored in the repo"),
+ SVN_TEST_OPTS_PASS(groups_authz,
+ "test authz with global groups"),
SVN_TEST_OPTS_PASS(commit_editor_authz,
"test authz in the commit editor"),
SVN_TEST_OPTS_PASS(commit_continue_txn,
Modified: subversion/trunk/tools/server-side/svnauthz.c
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/server-side/svnauthz.c?rev=1438407&r1=1438406&r2=1438407&view=diff
==============================================================================
--- subversion/trunk/tools/server-side/svnauthz.c (original)
+++ subversion/trunk/tools/server-side/svnauthz.c Fri Jan 25 09:59:30 2013
@@ -39,7 +39,8 @@ enum svnauthz__cmdline_options_t
svnauthz__username,
svnauthz__path,
svnauthz__repos,
- svnauthz__is
+ svnauthz__is,
+ svnauthz__groups_file
};
/* Option codes and descriptions.
@@ -66,6 +67,7 @@ static const apr_getopt_option_t options
" "
" no no access\n")
},
+ {"groups-file", svnauthz__groups_file, 1, ("path to the global groups file")},
{0, 0, 0, 0}
};
@@ -74,6 +76,7 @@ struct svnauthz_opt_state
svn_boolean_t help;
svn_boolean_t version;
const char *authz_file;
+ const char *groups_file;
const char *username;
const char *fspath;
const char *repos_name;
@@ -122,8 +125,9 @@ static const svn_opt_subcommand_desc2_t
{'t'} },
{"accessof", subcommand_accessof, {0} /* no aliases */,
("Print or test the permissions set by an authz file for a specific circumstance.\n"
- "usage: 1. svnauthz accessof [--username USER] TARGET\n"
- " 2. svnauthz accessof [--username USER] -t TXN REPOS_PATH FILE_PATH\n\n"
+ "usage: 1. svnauthz accessof [--username USER] [--groups-file GROUPS_FILE] TARGET\n"
+ " 2. svnauthz accessof [--username USER] [--groups-file GROUPS_FILE] \\\n"
+ " -t TXN REPOS_PATH FILE_PATH\n\n"
" 1. Prints the access of USER based on TARGET.\n"
" TARGET can be a path to a file or an absolute file:// URL to an authz\n"
" file in a repository, but cannot be a repository relative URL (^/).\n\n"
@@ -131,7 +135,8 @@ static const svn_opt_subcommand_desc2_t
" transaction TXN in the repository at REPOS_PATH.\n\n"
" If the --username argument is omitted then access of an anonymous user\n"
" will be printed. If --path argument is omitted prints if any access\n"
- " to the repo is allowed.\n\n"
+ " to the repo is allowed. If --groups-file is specified, the groups from\n"
+ " GROUPS_FILE will be used.\n\n"
"Outputs one of the following:\n"
" rw write access (which also implies read)\n"
" r read access\n"
@@ -142,7 +147,8 @@ static const svn_opt_subcommand_desc2_t
" 2 operational error\n"
" 3 when --is argument doesn't match\n"
),
- {'t', svnauthz__username, svnauthz__path, svnauthz__repos, svnauthz__is} },
+ {'t', svnauthz__username, svnauthz__path, svnauthz__repos, svnauthz__is,
+ svnauthz__groups_file} },
{ NULL, NULL, {0}, NULL, {0} }
};
@@ -178,20 +184,40 @@ subcommand_help(apr_getopt_t *os, void *
return SVN_NO_ERROR;
}
+/* Loads the fs FILENAME contents into *CONTENTS ensuring that the
+ corresponding node is a file. Using POOL for allocations. */
+static svn_error_t *
+read_file_contents(svn_stream_t **contents, const char *filename,
+ svn_fs_root_t *root, apr_pool_t *pool)
+{
+ svn_node_kind_t node_kind;
+
+ /* Make sure the path is a file */
+ SVN_ERR(svn_fs_check_path(&node_kind, root, filename, pool));
+ if (node_kind != svn_node_file)
+ return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+ "Path '%s' is not a file", filename);
+
+ SVN_ERR(svn_fs_file_contents(contents, root, filename, pool));
+
+ return SVN_NO_ERROR;
+}
+
/* Loads the authz config into *AUTHZ from the file at AUTHZ_FILE
- in repository at REPOS_PATH from the transaction TXN_NAME. Using
- POOL for allocations. */
+ in repository at REPOS_PATH from the transaction TXN_NAME. If GROUPS_FILE
+ is set, the resulting *AUTHZ will be constructed from AUTHZ_FILE with
+ global groups taken from GROUPS_FILE. Using POOL for allocations. */
static svn_error_t *
get_authz_from_txn(svn_authz_t **authz, const char *repos_path,
- const char *authz_file, const char *txn_name,
- apr_pool_t *pool)
+ const char *authz_file, const char *groups_file,
+ const char *txn_name, apr_pool_t *pool)
{
svn_repos_t *repos;
svn_fs_t *fs;
svn_fs_txn_t *txn;
svn_fs_root_t *root;
- svn_node_kind_t node_kind;
- svn_stream_t *contents;
+ svn_stream_t *authz_contents;
+ svn_stream_t *groups_contents;
svn_error_t *err;
/* Open up the repository and find the transaction root */
@@ -200,14 +226,16 @@ get_authz_from_txn(svn_authz_t **authz,
SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, pool));
SVN_ERR(svn_fs_txn_root(&root, txn, pool));
- /* Make sure the path is a file */
- SVN_ERR(svn_fs_check_path(&node_kind, root, authz_file, pool));
- if (node_kind != svn_node_file)
- return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
- "Path '%s' is not a file", authz_file);
+ /* Get the authz file contents. */
+ SVN_ERR(read_file_contents(&authz_contents, authz_file, root, pool));
- SVN_ERR(svn_fs_file_contents(&contents, root, authz_file, pool));
- err = svn_repos_authz_parse(authz, contents, pool);
+ /* Get the groups file contents if needed. */
+ if (groups_file)
+ SVN_ERR(read_file_contents(&groups_contents, groups_file, root, pool));
+ else
+ groups_contents = NULL;
+
+ err = svn_repos_authz_parse(authz, authz_contents, groups_contents, pool);
/* Add the filename to the error stack since the parser doesn't have it. */
if (err != SVN_NO_ERROR)
@@ -218,8 +246,10 @@ get_authz_from_txn(svn_authz_t **authz,
}
/* Loads the authz config into *AUTHZ from OPT_STATE->AUTHZ_FILE. If
- OPT_STATE->TXN is set then OPT_STATE->AUTHZ_FILE is treated as a fspath
- in repository at OPT_STATE->REPOS_PATH. */
+ OPT_STATE->GROUPS_FILE is set, loads the global groups from it.
+ If OPT_STATE->TXN is set then OPT_STATE->AUTHZ_FILE and
+ OPT_STATE->GROUPS_FILE are treated as fspaths in repository at
+ OPT_STATE->REPOS_PATH. */
static svn_error_t *
get_authz(svn_authz_t **authz, struct svnauthz_opt_state *opt_state,
apr_pool_t *pool)
@@ -227,10 +257,14 @@ get_authz(svn_authz_t **authz, struct sv
/* Read the access file and validate it. */
if (opt_state->txn)
return get_authz_from_txn(authz, opt_state->repos_path,
- opt_state->authz_file, opt_state->txn, pool);
+ opt_state->authz_file,
+ opt_state->groups_file,
+ opt_state->txn, pool);
/* Else */
- return svn_repos_authz_read2(authz, opt_state->authz_file, TRUE, NULL, pool);
+ return svn_repos_authz_read2(authz, opt_state->authz_file,
+ opt_state->groups_file,
+ TRUE, NULL, pool);
}
static svn_error_t *
@@ -374,6 +408,55 @@ use_compat_mode(const char *cmd, apr_poo
sizeof(SVNAUTHZ_COMPAT_NAME)-1);
}
+/* Canonicalize ACCESS_FILE into *CANONICALIZED_ACCESS_FILE based on the type
+ of argument. Error out on unsupported path types. If WITHIN_TXN is set,
+ ACCESS_FILE has to be a fspath in the repo. Use POOL for allocations. */
+static svn_error_t *
+canonicalize_access_file(const char **canonicalized_access_file,
+ const char *access_file,
+ svn_boolean_t within_txn,
+ apr_pool_t *pool)
+{
+ if (svn_path_is_repos_relative_url(access_file))
+ {
+ /* Can't accept repos relative urls since we don't have the path to
+ * the repository. */
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ ("'%s' is a repository relative URL when it "
+ "should be a local path or file:// URL"),
+ access_file);
+ }
+ else if (svn_path_is_url(access_file))
+ {
+ if (within_txn)
+ {
+ /* Don't allow urls with transaction argument. */
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ ("'%s' is a URL when it should be a "
+ "repository-relative path"),
+ access_file);
+ }
+
+ *canonicalized_access_file = svn_uri_canonicalize(access_file, pool);
+ }
+ else if (within_txn)
+ {
+ /* Transaction flag means this has to be a fspath to the access file
+ * in the repo. */
+ *canonicalized_access_file =
+ svn_fspath__canonicalize(access_file, pool);
+ }
+ else
+ {
+ /* If it isn't a URL and there's no transaction flag then it's a
+ * dirent to the access file on local disk. */
+ *canonicalized_access_file =
+ svn_dirent_internal_style(access_file, pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
static int
sub_main(int argc, const char *argv[], apr_pool_t *pool)
{
@@ -392,7 +475,7 @@ sub_main(int argc, const char *argv[], a
/* Initialize opt_state */
opt_state.username = opt_state.fspath = opt_state.repos_name = NULL;
- opt_state.txn = opt_state.repos_path = NULL;
+ opt_state.txn = opt_state.repos_path = opt_state.groups_file = NULL;
/* Parse options. */
SVN_INT_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
@@ -443,6 +526,11 @@ sub_main(int argc, const char *argv[], a
case svnauthz__is:
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.is, arg, pool));
break;
+ case svnauthz__groups_file:
+ SVN_INT_ERR(
+ svn_utf_cstring_to_utf8(&opt_state.groups_file,
+ arg, pool));
+ break;
default:
{
SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
@@ -543,47 +631,18 @@ sub_main(int argc, const char *argv[], a
SVN_INT_ERR(svn_utf_cstring_to_utf8(&opt_state.authz_file, os->argv[os->ind],
pool));
- /* Canonicalize opt_state.authz_file appropriately */
- if (svn_path_is_repos_relative_url(opt_state.authz_file))
- {
- /* Can't accept repos relative urls since we don't have the path to
- * the repository. */
- err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- ("'%s' is a repository relative URL when it "
- "should be a local path or file:// URL"),
- opt_state.authz_file);
- return EXIT_ERROR(err, EXIT_FAILURE);
- }
- else if (svn_path_is_url(opt_state.authz_file))
- {
- if (opt_state.txn)
- {
- /* don't allow urls with transaction argument */
- err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- ("'%s' is a URL when it should be a "
- "repository-relative path"),
- opt_state.authz_file);
- return EXIT_ERROR(err, EXIT_FAILURE);
- }
-
- opt_state.authz_file = svn_uri_canonicalize(opt_state.authz_file,
- pool);
+ /* Canonicalize opt_state.authz_file appropriately. */
+ SVN_INT_ERR(canonicalize_access_file(&opt_state.authz_file,
+ opt_state.authz_file,
+ opt_state.txn != NULL, pool));
+
+ /* Same for opt_state.groups_file if it is present. */
+ if (opt_state.groups_file)
+ {
+ SVN_INT_ERR(canonicalize_access_file(&opt_state.groups_file,
+ opt_state.groups_file,
+ opt_state.txn != NULL, pool));
}
- else if (opt_state.txn)
- {
- /* Transaction flag means this has to be a fspath to the authz_file
- * in the repo. */
- opt_state.authz_file =
- svn_fspath__canonicalize(opt_state.authz_file, pool);
- }
- else
- {
- /* If it isn't a URL and there's no transaction flag then it's a
- * dirent to a authz_file on local disk. */
- opt_state.authz_file = svn_dirent_internal_style(opt_state.authz_file,
- pool);
- }
-
}
/* Check that the subcommand wasn't passed any inappropriate options. */