summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/subversion
diff options
context:
space:
mode:
authorWenzong Fan <wenzong.fan@windriver.com>2015-11-17 00:38:41 -0500
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-12-01 21:32:05 +0000
commitf91aedfb56f121883f341958fe9228dc7e75cf2f (patch)
treea9664a1e2d42c412f4264890f5cf28d76a810d5b /meta/recipes-devtools/subversion
parent40cd22815cd252811db46a6768b0ea32db766f52 (diff)
downloadpoky-f91aedfb56f121883f341958fe9228dc7e75cf2f.tar.gz
subversion: fix CVE-2015-3184
mod_authz_svn in Apache Subversion 1.7.x before 1.7.21 and 1.8.x before 1.8.14, when using Apache httpd 2.4.x, does not properly restrict anonymous access, which allows remote anonymous users to read hidden files via the path name. Patch is from: http://subversion.apache.org/security/CVE-2015-3184-advisory.txt (From OE-Core rev: 29eb921ed074d86fa8d5b205a313eb3177473a63) Signed-off-by: Wenzong Fan <wenzong.fan@windriver.com> Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-devtools/subversion')
-rw-r--r--meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch2094
-rw-r--r--meta/recipes-devtools/subversion/subversion_1.8.13.bb1
2 files changed, 2095 insertions, 0 deletions
diff --git a/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch b/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch
new file mode 100644
index 0000000000..0663bd2719
--- /dev/null
+++ b/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch
@@ -0,0 +1,2094 @@
1Fix CVE-2015-3184
2
3Patch is from:
4http://subversion.apache.org/security/CVE-2015-3184-advisory.txt
5
6Upstream-Status: Backport
7
8Signed-off-by: Wenzong Fan <wenzong.fan@windriver.com>
9
10Index: Makefile.in
11===================================================================
12--- a/Makefile.in (revision 1691883)
13+++ b/Makefile.in (working copy)
14@@ -357,6 +357,7 @@ TEST_SHLIB_VAR_SWIG_RB=\
15 fi;
16
17 APXS = @APXS@
18+HTTPD_VERSION = @HTTPD_VERSION@
19
20 PYTHON = @PYTHON@
21 PERL = @PERL@
22@@ -509,6 +510,9 @@ check: bin @TRANSFORM_LIBTOOL_SCRIPTS@ $(TEST_DEPS
23 if test "$(HTTP_LIBRARY)" != ""; then \
24 flags="--http-library $(HTTP_LIBRARY) $$flags"; \
25 fi; \
26+ if test "$(HTTPD_VERSION)" != ""; then \
27+ flags="--httpd-version $(HTTPD_VERSION) $$flags"; \
28+ fi; \
29 if test "$(SERVER_MINOR_VERSION)" != ""; then \
30 flags="--server-minor-version $(SERVER_MINOR_VERSION) $$flags"; \
31 fi; \
32Index: build/ac-macros/apache.m4
33===================================================================
34--- a/build/ac-macros/apache.m4 (revision 1691883)
35+++ b/build/ac-macros/apache.m4 (working copy)
36@@ -160,6 +160,20 @@ if test -n "$APXS" && test "$APXS" != "no"; then
37 BUILD_APACHE_RULE=apache-mod
38 INSTALL_APACHE_RULE=install-mods-shared
39 INSTALL_APACHE_MODS=true
40+ HTTPD="`$APXS -q sbindir`/`$APXS -q PROGNAME`"
41+ if ! test -e $HTTPD ; then
42+ HTTPD="`$APXS -q bindir`/`$APXS -q PROGNAME`"
43+ fi
44+ HTTPD_VERSION=["`$HTTPD -v | $SED -e 's@^.*/\([0-9.]*\)\(.*$\)@\1@ ; 1q'`"]
45+ AC_ARG_ENABLE(broken-httpd-auth,
46+ AS_HELP_STRING([--enable-broken-httpd-auth],
47+ [Allow building against httpd 2.4 with broken auth]),
48+ [broken_httpd_auth=$enableval],[broken_httpd_auth=no])
49+ if test "$enable_broken_httpd_auth" = "yes"; then
50+ AC_MSG_NOTICE([Building with broken httpd auth])
51+ AC_DEFINE(SVN_ALLOW_BROKEN_HTTPD_AUTH, 1,
52+ [Defined to allow building against httpd 2.4 with broken auth])
53+ fi
54
55 case $host in
56 *-*-cygwin*)
57@@ -178,6 +192,7 @@ AC_SUBST(APACHE_LDFLAGS)
58 AC_SUBST(APACHE_INCLUDES)
59 AC_SUBST(APACHE_LIBEXECDIR)
60 AC_SUBST(INSTALL_APACHE_MODS)
61+AC_SUBST(HTTPD_VERSION)
62
63 # there aren't any flags that interest us ...
64 #if test -n "$APXS" && test "$APXS" != "no"; then
65Index: build/run_tests.py
66===================================================================
67--- a/build/run_tests.py (revision 1691883)
68+++ b/build/run_tests.py (working copy)
69@@ -29,6 +29,7 @@
70 [--fs-type=<fs-type>] [--fsfs-packing] [--fsfs-sharding=<n>]
71 [--list] [--milestone-filter=<regex>] [--mode-filter=<type>]
72 [--server-minor-version=<version>] [--http-proxy=<host>:<port>]
73+ [--httpd-version=<version>]
74 [--config-file=<file>] [--ssl-cert=<file>]
75 <abs_srcdir> <abs_builddir>
76 <prog ...>
77@@ -125,7 +126,7 @@ class TestHarness:
78 fsfs_sharding=None, fsfs_packing=None,
79 list_tests=None, svn_bin=None, mode_filter=None,
80 milestone_filter=None, set_log_level=None, ssl_cert=None,
81- http_proxy=None):
82+ http_proxy=None, httpd_version=None):
83 '''Construct a TestHarness instance.
84
85 ABS_SRCDIR and ABS_BUILDDIR are the source and build directories.
86@@ -178,6 +179,7 @@ class TestHarness:
87 self.log = None
88 self.ssl_cert = ssl_cert
89 self.http_proxy = http_proxy
90+ self.httpd_version = httpd_version
91 if not sys.stdout.isatty() or sys.platform == 'win32':
92 TextColors.disable()
93
94@@ -481,6 +483,8 @@ class TestHarness:
95 svntest.main.options.ssl_cert = self.ssl_cert
96 if self.http_proxy is not None:
97 svntest.main.options.http_proxy = self.http_proxy
98+ if self.httpd_version is not None:
99+ svntest.main.options.httpd_version = self.httpd_version
100
101 svntest.main.options.srcdir = self.srcdir
102
103@@ -645,7 +649,7 @@ def main():
104 'enable-sasl', 'parallel', 'config-file=',
105 'log-to-stdout', 'list', 'milestone-filter=',
106 'mode-filter=', 'set-log-level=', 'ssl-cert=',
107- 'http-proxy='])
108+ 'http-proxy=', 'httpd-version='])
109 except getopt.GetoptError:
110 args = []
111
112@@ -656,9 +660,9 @@ def main():
113 base_url, fs_type, verbose, cleanup, enable_sasl, http_library, \
114 server_minor_version, fsfs_sharding, fsfs_packing, parallel, \
115 config_file, log_to_stdout, list_tests, mode_filter, milestone_filter, \
116- set_log_level, ssl_cert, http_proxy = \
117+ set_log_level, ssl_cert, http_proxy, httpd_version = \
118 None, None, None, None, None, None, None, None, None, None, None, \
119- None, None, None, None, None, None, None
120+ None, None, None, None, None, None, None, None
121 for opt, val in opts:
122 if opt in ['-u', '--url']:
123 base_url = val
124@@ -696,6 +700,8 @@ def main():
125 ssl_cert = val
126 elif opt in ['--http-proxy']:
127 http_proxy = val
128+ elif opt in ['--httpd-version']:
129+ httpd_version = val
130 else:
131 raise getopt.GetoptError
132
133@@ -712,7 +718,7 @@ def main():
134 fsfs_sharding, fsfs_packing, list_tests,
135 mode_filter=mode_filter, milestone_filter=milestone_filter,
136 set_log_level=set_log_level, ssl_cert=ssl_cert,
137- http_proxy=http_proxy)
138+ http_proxy=http_proxy, httpd_version=httpd_version)
139
140 failed = th.run(args[2:])
141 if failed:
142Index: subversion/mod_authz_svn/mod_authz_svn.c
143===================================================================
144--- a/subversion/mod_authz_svn/mod_authz_svn.c (revision 1691883)
145+++ b/subversion/mod_authz_svn/mod_authz_svn.c (working copy)
146@@ -48,6 +48,23 @@
147 #include "svn_dirent_uri.h"
148 #include "private/svn_fspath.h"
149
150+/* The apache headers define these and they conflict with our definitions. */
151+#ifdef PACKAGE_BUGREPORT
152+#undef PACKAGE_BUGREPORT
153+#endif
154+#ifdef PACKAGE_NAME
155+#undef PACKAGE_NAME
156+#endif
157+#ifdef PACKAGE_STRING
158+#undef PACKAGE_STRING
159+#endif
160+#ifdef PACKAGE_TARNAME
161+#undef PACKAGE_TARNAME
162+#endif
163+#ifdef PACKAGE_VERSION
164+#undef PACKAGE_VERSION
165+#endif
166+#include "svn_private_config.h"
167
168 #ifdef APLOG_USE_MODULE
169 APLOG_USE_MODULE(authz_svn);
170@@ -67,6 +84,30 @@ typedef struct authz_svn_config_rec {
171 const char *force_username_case;
172 } authz_svn_config_rec;
173
174+#if AP_MODULE_MAGIC_AT_LEAST(20060110,0) /* version where
175+ ap_some_auth_required breaks */
176+# if AP_MODULE_MAGIC_AT_LEAST(20120211,47) /* first version with
177+ force_authn hook and
178+ ap_some_authn_required() which
179+ allows us to work without
180+ ap_some_auth_required() */
181+# define USE_FORCE_AUTHN 1
182+# define IN_SOME_AUTHN_NOTE "authz_svn-in-some-authn"
183+# define FORCE_AUTHN_NOTE "authz_svn-force-authn"
184+# else
185+ /* ap_some_auth_required() is busted and no viable alternative exists */
186+# ifndef SVN_ALLOW_BROKEN_HTTPD_AUTH
187+# error This version of httpd has a security hole with mod_authz_svn
188+# else
189+ /* user wants to build anyway */
190+# define USE_FORCE_AUTHN 0
191+# endif
192+# endif
193+#else
194+ /* old enough that ap_some_auth_required() still works */
195+# define USE_FORCE_AUTHN 0
196+#endif
197+
198 /*
199 * Configuration
200 */
201@@ -819,9 +860,51 @@ access_checker(request_rec *r)
202 &authz_svn_module);
203 const char *repos_path = NULL;
204 const char *dest_repos_path = NULL;
205- int status;
206+ int status, authn_required;
207
208+#if USE_FORCE_AUTHN
209+ /* Use the force_authn() hook available in 2.4.x to work securely
210+ * given that ap_some_auth_required() is no longer functional for our
211+ * purposes in 2.4.x.
212+ */
213+ int authn_configured;
214+
215 /* We are not configured to run */
216+ if (!conf->anonymous || apr_table_get(r->notes, IN_SOME_AUTHN_NOTE)
217+ || (! (conf->access_file || conf->repo_relative_access_file)))
218+ return DECLINED;
219+
220+ /* Authentication is configured */
221+ authn_configured = ap_auth_type(r) != NULL;
222+ if (authn_configured)
223+ {
224+ /* If the user is trying to authenticate, let him. It doesn't
225+ * make much sense to grant anonymous access but deny authenticated
226+ * users access, even though you can do that with '$anon' in the
227+ * access file.
228+ */
229+ if (apr_table_get(r->headers_in,
230+ (PROXYREQ_PROXY == r->proxyreq)
231+ ? "Proxy-Authorization" : "Authorization"))
232+ {
233+ /* Set the note to force authn regardless of what access_checker_ex
234+ hook requires */
235+ apr_table_setn(r->notes, FORCE_AUTHN_NOTE, (const char*)1);
236+
237+ /* provide the proper return so the access_checker hook doesn't
238+ * prevent the code from continuing on to the other auth hooks */
239+ if (ap_satisfies(r) != SATISFY_ANY)
240+ return OK;
241+ else
242+ return HTTP_FORBIDDEN;
243+ }
244+ }
245+
246+#else
247+ /* Support for older versions of httpd that have a working
248+ * ap_some_auth_required() */
249+
250+ /* We are not configured to run */
251 if (!conf->anonymous
252 || (! (conf->access_file || conf->repo_relative_access_file)))
253 return DECLINED;
254@@ -834,9 +917,10 @@ access_checker(request_rec *r)
255 if (ap_satisfies(r) != SATISFY_ANY)
256 return DECLINED;
257
258- /* If the user is trying to authenticate, let him. If anonymous
259- * access is allowed, so is authenticated access, by definition
260- * of the meaning of '*' in the access file.
261+ /* If the user is trying to authenticate, let him. It doesn't
262+ * make much sense to grant anonymous access but deny authenticated
263+ * users access, even though you can do that with '$anon' in the
264+ * access file.
265 */
266 if (apr_table_get(r->headers_in,
267 (PROXYREQ_PROXY == r->proxyreq)
268@@ -848,6 +932,7 @@ access_checker(request_rec *r)
269 return HTTP_FORBIDDEN;
270 }
271 }
272+#endif
273
274 /* If anon access is allowed, return OK */
275 status = req_check_access(r, conf, &repos_path, &dest_repos_path);
276@@ -856,7 +941,26 @@ access_checker(request_rec *r)
277 if (!conf->authoritative)
278 return DECLINED;
279
280+#if USE_FORCE_AUTHN
281+ if (authn_configured) {
282+ /* We have to check to see if authn is required because if so we must
283+ * return UNAUTHORIZED (401) rather than FORBIDDEN (403) since returning
284+ * the 403 leaks information about what paths may exist to
285+ * unauthenticated users. We must set a note here in order
286+ * to use ap_some_authn_rquired() without triggering an infinite
287+ * loop since the call will trigger this function to be called again. */
288+ apr_table_setn(r->notes, IN_SOME_AUTHN_NOTE, (const char*)1);
289+ authn_required = ap_some_authn_required(r);
290+ apr_table_unset(r->notes, IN_SOME_AUTHN_NOTE);
291+ if (authn_required)
292+ {
293+ ap_note_auth_failure(r);
294+ return HTTP_UNAUTHORIZED;
295+ }
296+ }
297+#else
298 if (!ap_some_auth_required(r))
299+#endif
300 log_access_verdict(APLOG_MARK, r, 0, repos_path, dest_repos_path);
301
302 return HTTP_FORBIDDEN;
303@@ -937,6 +1041,17 @@ auth_checker(request_rec *r)
304 return OK;
305 }
306
307+#if USE_FORCE_AUTHN
308+static int
309+force_authn(request_rec *r)
310+{
311+ if (apr_table_get(r->notes, FORCE_AUTHN_NOTE))
312+ return OK;
313+
314+ return DECLINED;
315+}
316+#endif
317+
318 /*
319 * Module flesh
320 */
321@@ -953,6 +1068,9 @@ register_hooks(apr_pool_t *p)
322 * give SSLOptions +FakeBasicAuth a chance to work. */
323 ap_hook_check_user_id(check_user_id, mod_ssl, NULL, APR_HOOK_FIRST);
324 ap_hook_auth_checker(auth_checker, NULL, NULL, APR_HOOK_FIRST);
325+#if USE_FORCE_AUTHN
326+ ap_hook_force_authn(force_authn, NULL, NULL, APR_HOOK_FIRST);
327+#endif
328 ap_register_provider(p,
329 AUTHZ_SVN__SUBREQ_BYPASS_PROV_GRP,
330 AUTHZ_SVN__SUBREQ_BYPASS_PROV_NAME,
331Index: subversion/tests/cmdline/README
332===================================================================
333--- a/subversion/tests/cmdline/README (revision 1691883)
334+++ b/subversion/tests/cmdline/README (working copy)
335@@ -83,6 +83,133 @@ paths adjusted appropriately:
336 Require valid-user
337 </Location>
338
339+ <Location /authz-test-work/anon>
340+ DAV svn
341+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
342+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
343+ SVNListParentPath On
344+ # This may seem unnecessary but granting access to everyone here is necessary
345+ # to exercise a bug with httpd 2.3.x+. The "Require all granted" syntax is
346+ # new to 2.3.x+ which we can detect with the mod_authz_core.c module
347+ # signature. Use the "Allow from all" syntax with older versions for symmetry.
348+ <IfModule mod_authz_core.c>
349+ Require all granted
350+ </IfModule>
351+ <IfModule !mod_authz_core.c>
352+ Allow from all
353+ </IfMOdule>
354+ </Location>
355+ <Location /authz-test-work/mixed>
356+ DAV svn
357+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
358+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
359+ SVNListParentPath On
360+ AuthType Basic
361+ AuthName "Subversion Repository"
362+ AuthUserFile /usr/local/apache2/conf/users
363+ Require valid-user
364+ Satisfy Any
365+ </Location>
366+ <Location /authz-test-work/mixed-noauthwhenanon>
367+ DAV svn
368+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
369+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
370+ SVNListParentPath On
371+ AuthType Basic
372+ AuthName "Subversion Repository"
373+ AuthUserFile /usr/local/apache2/conf/users
374+ Require valid-user
375+ AuthzSVNNoAuthWhenAnonymousAllowed On
376+ </Location>
377+ <Location /authz-test-work/authn>
378+ DAV svn
379+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
380+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
381+ SVNListParentPath On
382+ AuthType Basic
383+ AuthName "Subversion Repository"
384+ AuthUserFile /usr/local/apache2/conf/users
385+ Require valid-user
386+ </Location>
387+ <Location /authz-test-work/authn-anonoff>
388+ DAV svn
389+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
390+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
391+ SVNListParentPath On
392+ AuthType Basic
393+ AuthName "Subversion Repository"
394+ AuthUserFile /usr/local/apache2/conf/users
395+ Require valid-user
396+ AuthzSVNAnonymous Off
397+ </Location>
398+ <Location /authz-test-work/authn-lcuser>
399+ DAV svn
400+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
401+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
402+ SVNListParentPath On
403+ AuthType Basic
404+ AuthName "Subversion Repository"
405+ AuthUserFile /usr/local/apache2/conf/users
406+ Require valid-user
407+ AuthzForceUsernameCase Lower
408+ </Location>
409+ <Location /authz-test-work/authn-lcuser>
410+ DAV svn
411+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
412+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
413+ SVNListParentPath On
414+ AuthType Basic
415+ AuthName "Subversion Repository"
416+ AuthUserFile /usr/local/apache2/conf/users
417+ Require valid-user
418+ AuthzForceUsernameCase Lower
419+ </Location>
420+ <Location /authz-test-work/authn-group>
421+ DAV svn
422+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
423+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
424+ SVNListParentPath On
425+ AuthType Basic
426+ AuthName "Subversion Repository"
427+ AuthUserFile /usr/local/apache2/conf/users
428+ AuthGroupFile /usr/local/apache2/conf/groups
429+ Require group random
430+ AuthzSVNAuthoritative Off
431+ </Location>
432+ <IfModule mod_authz_core.c>
433+ <Location /authz-test-work/sallrany>
434+ DAV svn
435+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
436+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
437+ SVNListParentPath On
438+ AuthType Basic
439+ AuthName "Subversion Repository"
440+ AuthUserFile /usr/local/apache2/conf/users
441+ AuthzSendForbiddenOnFailure On
442+ Satisfy All
443+ <RequireAny>
444+ Require valid-user
445+ Require expr req('ALLOW') == '1'
446+ </RequireAny>
447+ </Location>
448+ <Location /authz-test-work/sallrall>
449+ DAV svn
450+ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp
451+ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz
452+ SVNListParentPath On
453+ AuthType Basic
454+ AuthName "Subversion Repository"
455+ AuthUserFile /usr/local/apache2/conf/users
456+ AuthzSendForbiddenOnFailure On
457+ Satisfy All
458+ <RequireAll>
459+ Require valid-user
460+ Require expr req('ALLOW') == '1'
461+ </RequireAll>
462+ </Location>
463+ </IfModule>
464+
465+
466 RedirectMatch permanent ^/svn-test-work/repositories/REDIRECT-PERM-(.*)$ /svn-test-work/repositories/$1
467 RedirectMatch ^/svn-test-work/repositories/REDIRECT-TEMP-(.*)$ /svn-test-work/repositories/$1
468
469@@ -101,8 +228,17 @@ just drop the following 2-line snippet into the
470 ----------------------------
471 jrandom:xCGl35kV9oWCY
472 jconstant:xCGl35kV9oWCY
473+JRANDOM:xCGl35kV9oWCY
474+JCONSTANT:xCGl35kV9oWCY
475 ----------------------------
476
477+and these lines into the
478+/usr/local/apache/conf/groups file:
479+----------------------------
480+random: jrandom
481+constant: jconstant
482+----------------------------
483+
484 Now, (re)start Apache and run the tests over mod_dav_svn.
485
486 You can run a test script over DAV:
487@@ -138,6 +274,8 @@ Note [1]: It would be quite too much to expect tho
488 ----------------------------
489 jrandom:$apr1$3p1.....$FQW6RceW5QhJ2blWDQgKn0
490 jconstant:$apr1$jp1.....$Usrqji1c9H6AbOxOGAzzb0
491+ JRANDOM:$apr1$3p1.....$FQW6RceW5QhJ2blWDQgKn0
492+ JCONSTANT:$apr1$jp1.....$Usrqji1c9H6AbOxOGAzzb0
493 ----------------------------
494
495
496Index: subversion/tests/cmdline/davautocheck.sh
497===================================================================
498--- a/subversion/tests/cmdline/davautocheck.sh (revision 1691883)
499+++ b/subversion/tests/cmdline/davautocheck.sh (working copy)
500@@ -289,8 +289,6 @@ LOAD_MOD_AUTHN_CORE="$(get_loadmodule_config mod_a
501 || fail "Authn_Core module not found."
502 LOAD_MOD_AUTHZ_CORE="$(get_loadmodule_config mod_authz_core)" \
503 || fail "Authz_Core module not found."
504-LOAD_MOD_AUTHZ_HOST="$(get_loadmodule_config mod_authz_host)" \
505- || fail "Authz_Host module not found."
506 LOAD_MOD_UNIXD=$(get_loadmodule_config mod_unixd) \
507 || fail "UnixD module not found"
508 }
509@@ -298,6 +296,10 @@ LOAD_MOD_AUTHN_FILE="$(get_loadmodule_config mod_a
510 || fail "Authn_File module not found."
511 LOAD_MOD_AUTHZ_USER="$(get_loadmodule_config mod_authz_user)" \
512 || fail "Authz_User module not found."
513+LOAD_MOD_AUTHZ_GROUPFILE="$(get_loadmodule_config mod_authz_groupfile)" \
514+ || fail "Authz_GroupFile module not found."
515+LOAD_MOD_AUTHZ_HOST="$(get_loadmodule_config mod_authz_host)" \
516+ || fail "Authz_Host module not found."
517 }
518 if [ ${APACHE_MPM:+set} ]; then
519 LOAD_MOD_MPM=$(get_loadmodule_config mod_mpm_$APACHE_MPM) \
520@@ -328,6 +330,7 @@ HTTPD_ERROR_LOG="$HTTPD_ROOT/error_log"
521 HTTPD_MIME_TYPES="$HTTPD_ROOT/mime.types"
522 BASE_URL="http://localhost:$HTTPD_PORT"
523 HTTPD_USERS="$HTTPD_ROOT/users"
524+HTTPD_GROUPS="$HTTPD_ROOT/groups"
525
526 mkdir "$HTTPD_ROOT" \
527 || fail "couldn't create temporary directory '$HTTPD_ROOT'"
528@@ -388,6 +391,14 @@ fi
529 say "Adding users for lock authentication"
530 $HTPASSWD -bc $HTTPD_USERS jrandom rayjandom
531 $HTPASSWD -b $HTTPD_USERS jconstant rayjandom
532+$HTPASSWD -b $HTTPD_USERS JRANDOM rayjandom
533+$HTPASSWD -b $HTTPD_USERS JCONSTANT rayjandom
534+
535+say "Adding groups for mod_authz_svn tests"
536+cat > "$HTTPD_GROUPS" <<__EOF__
537+random: jrandom
538+constant: jconstant
539+__EOF__
540
541 touch $HTTPD_MIME_TYPES
542
543@@ -405,7 +416,9 @@ $LOAD_MOD_AUTHN_CORE
544 $LOAD_MOD_AUTHN_FILE
545 $LOAD_MOD_AUTHZ_CORE
546 $LOAD_MOD_AUTHZ_USER
547+$LOAD_MOD_AUTHZ_GROUPFILE
548 $LOAD_MOD_AUTHZ_HOST
549+$LOAD_MOD_ACCESS_COMPAT
550 LoadModule authz_svn_module "$MOD_AUTHZ_SVN"
551
552 __EOF__
553@@ -497,6 +510,161 @@ CustomLog "$HTTPD_ROOT/ops" "%t %u %{SVN
554 SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
555 ${SVN_PATH_AUTHZ_LINE}
556 </Location>
557+<Location /authz-test-work/anon>
558+ DAV svn
559+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
560+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
561+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
562+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
563+ SVNListParentPath On
564+ # This may seem unnecessary but granting access to everyone here is necessary
565+ # to exercise a bug with httpd 2.3.x+. The "Require all granted" syntax is
566+ # new to 2.3.x+ which we can detect with the mod_authz_core.c module
567+ # signature. Use the "Allow from all" syntax with older versions for symmetry.
568+ <IfModule mod_authz_core.c>
569+ Require all granted
570+ </IfModule>
571+ <IfModule !mod_authz_core.c>
572+ Allow from all
573+ </IfMOdule>
574+ ${SVN_PATH_AUTHZ_LINE}
575+</Location>
576+<Location /authz-test-work/mixed>
577+ DAV svn
578+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
579+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
580+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
581+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
582+ SVNListParentPath On
583+ AuthType Basic
584+ AuthName "Subversion Repository"
585+ AuthUserFile $HTTPD_USERS
586+ Require valid-user
587+ Satisfy Any
588+ ${SVN_PATH_AUTHZ_LINE}
589+</Location>
590+<Location /authz-test-work/mixed-noauthwhenanon>
591+ DAV svn
592+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
593+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
594+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
595+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
596+ SVNListParentPath On
597+ AuthType Basic
598+ AuthName "Subversion Repository"
599+ AuthUserFile $HTTPD_USERS
600+ Require valid-user
601+ AuthzSVNNoAuthWhenAnonymousAllowed On
602+ SVNPathAuthz On
603+</Location>
604+<Location /authz-test-work/authn>
605+ DAV svn
606+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
607+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
608+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
609+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
610+ SVNListParentPath On
611+ AuthType Basic
612+ AuthName "Subversion Repository"
613+ AuthUserFile $HTTPD_USERS
614+ Require valid-user
615+ ${SVN_PATH_AUTHZ_LINE}
616+</Location>
617+<Location /authz-test-work/authn-anonoff>
618+ DAV svn
619+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
620+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
621+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
622+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
623+ SVNListParentPath On
624+ AuthType Basic
625+ AuthName "Subversion Repository"
626+ AuthUserFile $HTTPD_USERS
627+ Require valid-user
628+ AuthzSVNAnonymous Off
629+ SVNPathAuthz On
630+</Location>
631+<Location /authz-test-work/authn-lcuser>
632+ DAV svn
633+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
634+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
635+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
636+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
637+ SVNListParentPath On
638+ AuthType Basic
639+ AuthName "Subversion Repository"
640+ AuthUserFile $HTTPD_USERS
641+ Require valid-user
642+ AuthzForceUsernameCase Lower
643+ ${SVN_PATH_AUTHZ_LINE}
644+</Location>
645+<Location /authz-test-work/authn-lcuser>
646+ DAV svn
647+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
648+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
649+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
650+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
651+ SVNListParentPath On
652+ AuthType Basic
653+ AuthName "Subversion Repository"
654+ AuthUserFile $HTTPD_USERS
655+ Require valid-user
656+ AuthzForceUsernameCase Lower
657+ ${SVN_PATH_AUTHZ_LINE}
658+</Location>
659+<Location /authz-test-work/authn-group>
660+ DAV svn
661+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
662+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
663+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
664+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
665+ SVNListParentPath On
666+ AuthType Basic
667+ AuthName "Subversion Repository"
668+ AuthUserFile $HTTPD_USERS
669+ AuthGroupFile $HTTPD_GROUPS
670+ Require group random
671+ AuthzSVNAuthoritative Off
672+ SVNPathAuthz On
673+</Location>
674+<IfModule mod_authz_core.c>
675+ <Location /authz-test-work/sallrany>
676+ DAV svn
677+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
678+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
679+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
680+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
681+ SVNListParentPath On
682+ AuthType Basic
683+ AuthName "Subversion Repository"
684+ AuthUserFile $HTTPD_USERS
685+ AuthzSendForbiddenOnFailure On
686+ Satisfy All
687+ <RequireAny>
688+ Require valid-user
689+ Require expr req('ALLOW') == '1'
690+ </RequireAny>
691+ ${SVN_PATH_AUTHZ_LINE}
692+ </Location>
693+ <Location /authz-test-work/sallrall>
694+ DAV svn
695+ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp"
696+ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz"
697+ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL}
698+ SVNCacheRevProps ${CACHE_REVPROPS_SETTING}
699+ SVNListParentPath On
700+ AuthType Basic
701+ AuthName "Subversion Repository"
702+ AuthUserFile $HTTPD_USERS
703+ AuthzSendForbiddenOnFailure On
704+ Satisfy All
705+ <RequireAll>
706+ Require valid-user
707+ Require expr req('ALLOW') == '1'
708+ </RequireAll>
709+ ${SVN_PATH_AUTHZ_LINE}
710+ </Location>
711+</IfModule>
712 RedirectMatch permanent ^/svn-test-work/repositories/REDIRECT-PERM-(.*)\$ /svn-test-work/repositories/\$1
713 RedirectMatch ^/svn-test-work/repositories/REDIRECT-TEMP-(.*)\$ /svn-test-work/repositories/\$1
714 __EOF__
715Index: subversion/tests/cmdline/mod_authz_svn_tests.py
716===================================================================
717--- a/subversion/tests/cmdline/mod_authz_svn_tests.py (nonexistent)
718+++ b/subversion/tests/cmdline/mod_authz_svn_tests.py (working copy)
719@@ -0,0 +1,1073 @@
720+#!/usr/bin/env python
721+#
722+# mod_authz_svn_tests.py: testing mod_authz_svn
723+#
724+# Subversion is a tool for revision control.
725+# See http://subversion.apache.org for more information.
726+#
727+# ====================================================================
728+# Licensed to the Apache Software Foundation (ASF) under one
729+# or more contributor license agreements. See the NOTICE file
730+# distributed with this work for additional information
731+# regarding copyright ownership. The ASF licenses this file
732+# to you under the Apache License, Version 2.0 (the
733+# "License"); you may not use this file except in compliance
734+# with the License. You may obtain a copy of the License at
735+#
736+# http://www.apache.org/licenses/LICENSE-2.0
737+#
738+# Unless required by applicable law or agreed to in writing,
739+# software distributed under the License is distributed on an
740+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
741+# KIND, either express or implied. See the License for the
742+# specific language governing permissions and limitations
743+# under the License.
744+######################################################################
745+
746+# General modules
747+import os, re, logging
748+
749+logger = logging.getLogger()
750+
751+# Our testing module
752+import svntest
753+
754+# (abbreviation)
755+Skip = svntest.testcase.Skip_deco
756+SkipUnless = svntest.testcase.SkipUnless_deco
757+XFail = svntest.testcase.XFail_deco
758+Issues = svntest.testcase.Issues_deco
759+Issue = svntest.testcase.Issue_deco
760+Wimp = svntest.testcase.Wimp_deco
761+
762+ls_of_D_no_H = '''<html><head><title>repos - Revision 1: /A/D</title></head>
763+<body>
764+ <h2>repos - Revision 1: /A/D</h2>
765+ <ul>
766+ <li><a href="../">..</a></li>
767+ <li><a href="G/">G/</a></li>
768+ <li><a href="gamma">gamma</a></li>
769+ </ul>
770+</body></html>'''
771+
772+ls_of_D_H = '''<html><head><title>repos - Revision 1: /A/D</title></head>
773+<body>
774+ <h2>repos - Revision 1: /A/D</h2>
775+ <ul>
776+ <li><a href="../">..</a></li>
777+ <li><a href="G/">G/</a></li>
778+ <li><a href="H/">H/</a></li>
779+ <li><a href="gamma">gamma</a></li>
780+ </ul>
781+</body></html>'''
782+
783+ls_of_H = '''<html><head><title>repos - Revision 1: /A/D/H</title></head>
784+<body>
785+ <h2>repos - Revision 1: /A/D/H</h2>
786+ <ul>
787+ <li><a href="../">..</a></li>
788+ <li><a href="chi">chi</a></li>
789+ <li><a href="omega">omega</a></li>
790+ <li><a href="psi">psi</a></li>
791+ </ul>
792+</body></html>'''
793+
794+user1 = svntest.main.wc_author
795+user1_upper = user1.upper()
796+user1_pass = svntest.main.wc_passwd
797+user1_badpass = 'XXX'
798+assert user1_pass != user1_badpass, "Passwords can't match"
799+user2 = svntest.main.wc_author2
800+user2_upper = user2.upper()
801+user2_pass = svntest.main.wc_passwd
802+user2_badpass = 'XXX'
803+assert user2_pass != user2_badpass, "Passwords can't match"
804+
805+def write_authz_file(sbox):
806+ svntest.main.write_authz_file(sbox, {
807+ '/': '$anonymous = r\n' +
808+ 'jrandom = rw\n' +
809+ 'jconstant = rw',
810+ '/A/D/H': '$anonymous =\n' +
811+ '$authenticated =\n' +
812+ 'jrandom = rw'
813+ })
814+
815+def write_authz_file_groups(sbox):
816+ authz_name = sbox.authz_name()
817+ svntest.main.write_authz_file(sbox,{
818+ '/': '* =',
819+ })
820+
821+def verify_get(test_area_url, path, user, pw,
822+ expected_status, expected_body, headers):
823+ import httplib
824+ from urlparse import urlparse
825+ import base64
826+
827+ req_url = test_area_url + path
828+
829+ loc = urlparse(req_url)
830+
831+ if loc.scheme == 'http':
832+ h = httplib.HTTPConnection(loc.hostname, loc.port)
833+ else:
834+ h = httplib.HTTPSConnection(loc.hostname, loc.port)
835+
836+ if headers is None:
837+ headers = {}
838+
839+ if user and pw:
840+ auth_info = user + ':' + pw
841+ headers['Authorization'] = 'Basic ' + base64.b64encode(auth_info)
842+ else:
843+ auth_info = "anonymous"
844+
845+ h.request('GET', req_url, None, headers)
846+
847+ r = h.getresponse()
848+
849+ actual_status = r.status
850+ if expected_status and expected_status != actual_status:
851+
852+ logger.warn("Expected status '" + str(expected_status) +
853+ "' but got '" + str(actual_status) +
854+ "' on url '" + req_url + "' (" +
855+ auth_info + ").")
856+ raise svntest.Failure
857+
858+ if expected_body:
859+ actual_body = r.read()
860+ if expected_body != actual_body:
861+ logger.warn("Expected body:")
862+ logger.warn(expected_body)
863+ logger.warn("But got:")
864+ logger.warn(actual_body)
865+ logger.warn("on url '" + req_url + "' (" + auth_info + ").")
866+ raise svntest.Failure
867+
868+def verify_gets(test_area_url, tests):
869+ for test in tests:
870+ verify_get(test_area_url, test['path'], test.get('user'), test.get('pw'),
871+ test['status'], test.get('body'), test.get('headers'))
872+
873+
874+######################################################################
875+# Tests
876+#
877+# Each test must return on success or raise on failure.
878+
879+
880+#----------------------------------------------------------------------
881+
882+
883+@SkipUnless(svntest.main.is_ra_type_dav)
884+def anon(sbox):
885+ "test anonymous access"
886+ sbox.build(read_only = True, create_wc = False)
887+
888+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
889+ '/authz-test-work/anon')
890+
891+ write_authz_file(sbox)
892+
893+ anon_tests = (
894+ { 'path': '', 'status': 301 },
895+ { 'path': '/', 'status': 200 },
896+ { 'path': '/repos', 'status': 301 },
897+ { 'path': '/repos/', 'status': 200 },
898+ { 'path': '/repos/A', 'status': 301 },
899+ { 'path': '/repos/A/', 'status': 200 },
900+ { 'path': '/repos/A/D', 'status': 301 },
901+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H },
902+ { 'path': '/repos/A/D/gamma', 'status': 200 },
903+ { 'path': '/repos/A/D/H', 'status': 403 },
904+ { 'path': '/repos/A/D/H/', 'status': 403 },
905+ { 'path': '/repos/A/D/H/chi', 'status': 403 },
906+ # auth isn't configured so nothing should change when passing
907+ # authn details
908+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass},
909+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass},
910+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass},
911+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass},
912+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass},
913+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass},
914+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass},
915+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
916+ 'user': user1, 'pw': user1_pass},
917+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass},
918+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_pass},
919+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_pass},
920+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_pass},
921+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_badpass},
922+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_badpass},
923+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_badpass},
924+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_badpass},
925+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_badpass},
926+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_badpass},
927+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_badpass},
928+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
929+ 'user': user1, 'pw': user1_badpass},
930+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_badpass},
931+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_badpass},
932+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_badpass},
933+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_badpass},
934+ { 'path': '', 'status': 301, 'user': user2, 'pw': user1_pass},
935+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user1_pass},
936+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user1_pass},
937+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user1_pass},
938+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user1_pass},
939+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user1_pass},
940+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user1_pass},
941+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
942+ 'user': user2, 'pw': user1_pass},
943+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass},
944+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass},
945+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass},
946+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass},
947+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_badpass},
948+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_badpass},
949+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_badpass},
950+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_badpass},
951+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_badpass},
952+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_badpass},
953+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_badpass},
954+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
955+ 'user': user2, 'pw': user2_badpass},
956+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_badpass},
957+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_badpass},
958+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_badpass},
959+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_badpass},
960+ )
961+
962+ verify_gets(test_area_url, anon_tests)
963+
964+
965+@SkipUnless(svntest.main.is_ra_type_dav)
966+def mixed(sbox):
967+ "test mixed anonymous and authenticated access"
968+ sbox.build(read_only = True, create_wc = False)
969+
970+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
971+ '/authz-test-work/mixed')
972+
973+ write_authz_file(sbox)
974+
975+ mixed_tests = (
976+ { 'path': '', 'status': 301, },
977+ { 'path': '/', 'status': 200, },
978+ { 'path': '/repos', 'status': 301, },
979+ { 'path': '/repos/', 'status': 200, },
980+ { 'path': '/repos/A', 'status': 301, },
981+ { 'path': '/repos/A/', 'status': 200, },
982+ { 'path': '/repos/A/D', 'status': 301, },
983+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
984+ },
985+ { 'path': '/repos/A/D/gamma', 'status': 200, },
986+ { 'path': '/repos/A/D/H', 'status': 401, },
987+ { 'path': '/repos/A/D/H/', 'status': 401, },
988+ { 'path': '/repos/A/D/H/chi', 'status': 401, },
989+ # auth is configured and user1 is allowed access to H
990+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass},
991+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass},
992+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass},
993+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass},
994+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass},
995+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass},
996+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass},
997+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
998+ 'user': user1, 'pw': user1_pass},
999+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass},
1000+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass},
1001+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass},
1002+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass},
1003+ # try with the wrong password for user1
1004+ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass},
1005+ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1006+ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass},
1007+ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1008+ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass},
1009+ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1010+ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass},
1011+ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1012+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass},
1013+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass},
1014+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1015+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass},
1016+ # auth is configured and user2 is not allowed access to H
1017+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass},
1018+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass},
1019+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass},
1020+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass},
1021+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass},
1022+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass},
1023+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass},
1024+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1025+ 'user': user2, 'pw': user2_pass},
1026+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass},
1027+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass},
1028+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass},
1029+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass},
1030+ # try with the wrong password for user2
1031+ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass},
1032+ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1033+ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass},
1034+ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1035+ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass},
1036+ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1037+ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass},
1038+ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1039+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass},
1040+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass},
1041+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1042+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass},
1043+ )
1044+
1045+ verify_gets(test_area_url, mixed_tests)
1046+
1047+@SkipUnless(svntest.main.is_ra_type_dav)
1048+@XFail(svntest.main.is_httpd_authz_provider_enabled)
1049+# uses the AuthzSVNNoAuthWhenAnonymousAllowed On directive
1050+# this is broken with httpd 2.3.x+ since it requires the auth system to accept
1051+# r->user == NULL and there is a test for this in server/request.c now. It
1052+# was intended as a workaround for the lack of Satisfy Any in 2.3.x+ which
1053+# was resolved by httpd with mod_access_compat in 2.3.x+.
1054+def mixed_noauthwhenanon(sbox):
1055+ "test mixed with noauthwhenanon directive"
1056+ sbox.build(read_only = True, create_wc = False)
1057+
1058+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
1059+ '/authz-test-work/mixed-noauthwhenanon')
1060+
1061+ write_authz_file(sbox)
1062+
1063+ noauthwhenanon_tests = (
1064+ { 'path': '', 'status': 301, },
1065+ { 'path': '/', 'status': 200, },
1066+ { 'path': '/repos', 'status': 301, },
1067+ { 'path': '/repos/', 'status': 200, },
1068+ { 'path': '/repos/A', 'status': 301, },
1069+ { 'path': '/repos/A/', 'status': 200, },
1070+ { 'path': '/repos/A/D', 'status': 301, },
1071+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1072+ },
1073+ { 'path': '/repos/A/D/gamma', 'status': 200, },
1074+ { 'path': '/repos/A/D/H', 'status': 401, },
1075+ { 'path': '/repos/A/D/H/', 'status': 401, },
1076+ { 'path': '/repos/A/D/H/chi', 'status': 401, },
1077+ # auth is configured and user1 is allowed access to H
1078+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass},
1079+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass},
1080+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass},
1081+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass},
1082+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass},
1083+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass},
1084+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass},
1085+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1086+ 'user': user1, 'pw': user1_pass},
1087+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass},
1088+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass},
1089+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass},
1090+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass},
1091+ # try with the wrong password for user1
1092+ # note that unlike doing this with Satisfy Any this case
1093+ # actually provides anon access when provided with an invalid
1094+ # password
1095+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_badpass},
1096+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_badpass},
1097+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_badpass},
1098+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_badpass},
1099+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_badpass},
1100+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_badpass},
1101+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_badpass},
1102+ { 'path': '/repos/A/D/', 'status': 200, 'user': user1, 'pw': user1_badpass},
1103+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_badpass},
1104+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass},
1105+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1106+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass},
1107+ # auth is configured and user2 is not allowed access to H
1108+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass},
1109+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass},
1110+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass},
1111+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass},
1112+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass},
1113+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass},
1114+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass},
1115+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1116+ 'user': user2, 'pw': user2_pass},
1117+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass},
1118+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass},
1119+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass},
1120+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass},
1121+ # try with the wrong password for user2
1122+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_badpass},
1123+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_badpass},
1124+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_badpass},
1125+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_badpass},
1126+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_badpass},
1127+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_badpass},
1128+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_badpass},
1129+ { 'path': '/repos/A/D/', 'status': 200, 'user': user2, 'pw': user2_badpass},
1130+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_badpass},
1131+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass},
1132+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1133+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass},
1134+ )
1135+
1136+ verify_gets(test_area_url, noauthwhenanon_tests)
1137+
1138+
1139+@SkipUnless(svntest.main.is_ra_type_dav)
1140+def authn(sbox):
1141+ "test authenticated only access"
1142+ sbox.build(read_only = True, create_wc = False)
1143+
1144+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
1145+ '/authz-test-work/authn')
1146+
1147+ write_authz_file(sbox)
1148+
1149+ authn_tests = (
1150+ { 'path': '', 'status': 401, },
1151+ { 'path': '/', 'status': 401, },
1152+ { 'path': '/repos', 'status': 401, },
1153+ { 'path': '/repos/', 'status': 401, },
1154+ { 'path': '/repos/A', 'status': 401, },
1155+ { 'path': '/repos/A/', 'status': 401, },
1156+ { 'path': '/repos/A/D', 'status': 401, },
1157+ { 'path': '/repos/A/D/', 'status': 401, },
1158+ { 'path': '/repos/A/D/gamma', 'status': 401, },
1159+ { 'path': '/repos/A/D/H', 'status': 401, },
1160+ { 'path': '/repos/A/D/H/', 'status': 401, },
1161+ { 'path': '/repos/A/D/H/chi', 'status': 401, },
1162+ # auth is configured and user1 is allowed access to H
1163+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass},
1164+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass},
1165+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass},
1166+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass},
1167+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass},
1168+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass},
1169+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass},
1170+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1171+ 'user': user1, 'pw': user1_pass},
1172+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass},
1173+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass},
1174+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass},
1175+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass},
1176+ # try with upper case username for user1
1177+ { 'path': '', 'status': 301, 'user': user1_upper, 'pw': user1_pass},
1178+ { 'path': '/', 'status': 200, 'user': user1_upper, 'pw': user1_pass},
1179+ { 'path': '/repos', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1180+ { 'path': '/repos/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1181+ { 'path': '/repos/A', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1182+ { 'path': '/repos/A/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1183+ { 'path': '/repos/A/D', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1184+ { 'path': '/repos/A/D/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1185+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1186+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1187+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1188+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1189+ # try with the wrong password for user1
1190+ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass},
1191+ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1192+ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass},
1193+ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1194+ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass},
1195+ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1196+ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass},
1197+ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1198+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass},
1199+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass},
1200+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1201+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass},
1202+ # auth is configured and user2 is not allowed access to H
1203+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass},
1204+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass},
1205+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass},
1206+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass},
1207+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass},
1208+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass},
1209+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass},
1210+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1211+ 'user': user2, 'pw': user2_pass},
1212+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass},
1213+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass},
1214+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass},
1215+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass},
1216+ # try with upper case username for user2
1217+ { 'path': '', 'status': 301, 'user': user2_upper, 'pw': user2_pass},
1218+ { 'path': '/', 'status': 200, 'user': user2_upper, 'pw': user2_pass},
1219+ { 'path': '/repos', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1220+ { 'path': '/repos/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1221+ { 'path': '/repos/A', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1222+ { 'path': '/repos/A/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1223+ { 'path': '/repos/A/D', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1224+ { 'path': '/repos/A/D/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1225+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1226+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1227+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1228+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1229+ # try with the wrong password for user2
1230+ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass},
1231+ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1232+ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass},
1233+ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1234+ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass},
1235+ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1236+ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass},
1237+ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1238+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass},
1239+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass},
1240+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1241+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass},
1242+ )
1243+
1244+ verify_gets(test_area_url, authn_tests)
1245+
1246+@SkipUnless(svntest.main.is_ra_type_dav)
1247+def authn_anonoff(sbox):
1248+ "test authenticated only access with anonoff"
1249+ sbox.build(read_only = True, create_wc = False)
1250+
1251+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
1252+ '/authz-test-work/authn-anonoff')
1253+
1254+ write_authz_file(sbox)
1255+
1256+ anonoff_tests = (
1257+ { 'path': '', 'status': 401, },
1258+ { 'path': '/', 'status': 401, },
1259+ { 'path': '/repos', 'status': 401, },
1260+ { 'path': '/repos/', 'status': 401, },
1261+ { 'path': '/repos/A', 'status': 401, },
1262+ { 'path': '/repos/A/', 'status': 401, },
1263+ { 'path': '/repos/A/D', 'status': 401, },
1264+ { 'path': '/repos/A/D/', 'status': 401, },
1265+ { 'path': '/repos/A/D/gamma', 'status': 401, },
1266+ { 'path': '/repos/A/D/H', 'status': 401, },
1267+ { 'path': '/repos/A/D/H/', 'status': 401, },
1268+ { 'path': '/repos/A/D/H/chi', 'status': 401, },
1269+ # auth is configured and user1 is allowed access to H
1270+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass},
1271+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass},
1272+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass},
1273+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass},
1274+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass},
1275+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass},
1276+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass},
1277+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1278+ 'user': user1, 'pw': user1_pass},
1279+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass},
1280+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass},
1281+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass},
1282+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass},
1283+ # try with upper case username for user1
1284+ { 'path': '', 'status': 301, 'user': user1_upper, 'pw': user1_pass},
1285+ { 'path': '/', 'status': 200, 'user': user1_upper, 'pw': user1_pass},
1286+ { 'path': '/repos', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1287+ { 'path': '/repos/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1288+ { 'path': '/repos/A', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1289+ { 'path': '/repos/A/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1290+ { 'path': '/repos/A/D', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1291+ { 'path': '/repos/A/D/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1292+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1293+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1294+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1295+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1_upper, 'pw': user1_pass},
1296+ # try with the wrong password for user1
1297+ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass},
1298+ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1299+ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass},
1300+ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1301+ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass},
1302+ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1303+ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass},
1304+ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1305+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass},
1306+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass},
1307+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1308+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass},
1309+ # auth is configured and user2 is not allowed access to H
1310+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass},
1311+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass},
1312+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass},
1313+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass},
1314+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass},
1315+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass},
1316+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass},
1317+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1318+ 'user': user2, 'pw': user2_pass},
1319+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass},
1320+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass},
1321+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass},
1322+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass},
1323+ # try with upper case username for user2
1324+ { 'path': '', 'status': 301, 'user': user2_upper, 'pw': user2_pass},
1325+ { 'path': '/', 'status': 200, 'user': user2_upper, 'pw': user2_pass},
1326+ { 'path': '/repos', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1327+ { 'path': '/repos/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1328+ { 'path': '/repos/A', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1329+ { 'path': '/repos/A/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1330+ { 'path': '/repos/A/D', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1331+ { 'path': '/repos/A/D/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1332+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1333+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1334+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1335+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1336+ # try with the wrong password for user2
1337+ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass},
1338+ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1339+ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass},
1340+ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1341+ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass},
1342+ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1343+ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass},
1344+ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1345+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass},
1346+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass},
1347+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1348+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass},
1349+ )
1350+
1351+ verify_gets(test_area_url, anonoff_tests)
1352+
1353+@SkipUnless(svntest.main.is_ra_type_dav)
1354+def authn_lcuser(sbox):
1355+ "test authenticated only access with lcuser"
1356+ sbox.build(read_only = True, create_wc = False)
1357+
1358+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
1359+ '/authz-test-work/authn-lcuser')
1360+
1361+ write_authz_file(sbox)
1362+
1363+ lcuser_tests = (
1364+ # try with upper case username for user1 (works due to lcuser option)
1365+ { 'path': '', 'status': 301, 'user': user1_upper, 'pw': user1_pass},
1366+ { 'path': '/', 'status': 200, 'user': user1_upper, 'pw': user1_pass},
1367+ { 'path': '/repos', 'status': 301, 'user': user1_upper, 'pw': user1_pass},
1368+ { 'path': '/repos/', 'status': 200, 'user': user1_upper, 'pw': user1_pass},
1369+ { 'path': '/repos/A', 'status': 301, 'user': user1_upper, 'pw': user1_pass},
1370+ { 'path': '/repos/A/', 'status': 200, 'user': user1_upper, 'pw': user1_pass},
1371+ { 'path': '/repos/A/D', 'status': 301, 'user': user1_upper, 'pw': user1_pass},
1372+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1373+ 'user': user1_upper, 'pw': user1_pass},
1374+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1_upper, 'pw': user1_pass},
1375+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1_upper, 'pw': user1_pass},
1376+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1_upper, 'pw': user1_pass},
1377+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1_upper, 'pw': user1_pass},
1378+ # try with upper case username for user2 (works due to lcuser option)
1379+ { 'path': '', 'status': 301, 'user': user2_upper, 'pw': user2_pass},
1380+ { 'path': '/', 'status': 200, 'user': user2_upper, 'pw': user2_pass},
1381+ { 'path': '/repos', 'status': 301, 'user': user2_upper, 'pw': user2_pass},
1382+ { 'path': '/repos/', 'status': 200, 'user': user2_upper, 'pw': user2_pass},
1383+ { 'path': '/repos/A', 'status': 301, 'user': user2_upper, 'pw': user2_pass},
1384+ { 'path': '/repos/A/', 'status': 200, 'user': user2_upper, 'pw': user2_pass},
1385+ { 'path': '/repos/A/D', 'status': 301, 'user': user2_upper, 'pw': user2_pass},
1386+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1387+ 'user': user2_upper, 'pw': user2_pass},
1388+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2_upper, 'pw': user2_pass},
1389+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1390+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1391+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2_upper, 'pw': user2_pass},
1392+ )
1393+
1394+ verify_gets(test_area_url, lcuser_tests)
1395+
1396+# authenticated access only by group - a excuse to use AuthzSVNAuthoritative Off
1397+# this is terribly messed up, Require group runs after mod_authz_svn.
1398+# so if mod_authz_svn grants the access then it doesn't matter what the group
1399+# requirement says. If we reject the access then you can use the AuthzSVNAuthoritative Off
1400+# directive to fall through to the group check. Overall the behavior of setups like this
1401+# is almost guaranteed to not be what users expect.
1402+@SkipUnless(svntest.main.is_ra_type_dav)
1403+def authn_group(sbox):
1404+ "test authenticated only access via groups"
1405+ sbox.build(read_only = True, create_wc = False)
1406+
1407+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
1408+ '/authz-test-work/authn-group')
1409+
1410+ # Can't use write_authz_file() as most tests because we want to deny all
1411+ # access with mod_authz_svn so the tests fall through to the group handling
1412+ authz_name = sbox.authz_name()
1413+ svntest.main.write_authz_file(sbox, {
1414+ '/': '* =',
1415+ })
1416+
1417+ group_tests = (
1418+ { 'path': '', 'status': 401, },
1419+ { 'path': '/', 'status': 401, },
1420+ { 'path': '/repos', 'status': 401, },
1421+ { 'path': '/repos/', 'status': 401, },
1422+ { 'path': '/repos/A', 'status': 401, },
1423+ { 'path': '/repos/A/', 'status': 401, },
1424+ { 'path': '/repos/A/D', 'status': 401, },
1425+ { 'path': '/repos/A/D/', 'status': 401, },
1426+ { 'path': '/repos/A/D/gamma', 'status': 401, },
1427+ { 'path': '/repos/A/D/H', 'status': 401, },
1428+ { 'path': '/repos/A/D/H/', 'status': 401, },
1429+ { 'path': '/repos/A/D/H/chi', 'status': 401, },
1430+ # auth is configured and user1 is allowed access repo including H
1431+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass},
1432+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass},
1433+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass},
1434+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass},
1435+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass},
1436+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass},
1437+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass},
1438+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1439+ 'user': user1, 'pw': user1_pass},
1440+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass},
1441+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass},
1442+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass},
1443+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass},
1444+ )
1445+
1446+ verify_gets(test_area_url, group_tests)
1447+
1448+# This test exists to validate our behavior when used with the new authz
1449+# provider system introduced in httpd 2.3.x. The Satisfy directive
1450+# determines how older authz hooks are combined and the RequireA(ll|ny)
1451+# blocks handles how new authz providers are combined. The overall results of
1452+# all the authz providers (combined per the Require* blocks) are then
1453+# combined with the other authz hooks via the Satisfy directive.
1454+# Meaning this test requires that mod_authz_svn says yes and there is
1455+# either a valid user or the ALLOW header is 1. The header may seem
1456+# like a silly test but it's easier to excercise than say a host directive
1457+# in a repeatable test.
1458+@SkipUnless(svntest.main.is_httpd_authz_provider_enabled)
1459+def authn_sallrany(sbox):
1460+ "test satisfy all require any config"
1461+ sbox.build(read_only = True, create_wc = False)
1462+
1463+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
1464+ '/authz-test-work/sallrany')
1465+
1466+ write_authz_file(sbox)
1467+
1468+ allow_header = { 'ALLOW': '1' }
1469+
1470+ sallrany_tests = (
1471+ #anon access isn't allowed without ALLOW header
1472+ { 'path': '', 'status': 401, },
1473+ { 'path': '/', 'status': 401, },
1474+ { 'path': '/repos', 'status': 401, },
1475+ { 'path': '/repos/', 'status': 401, },
1476+ { 'path': '/repos/A', 'status': 401, },
1477+ { 'path': '/repos/A/', 'status': 401, },
1478+ { 'path': '/repos/A/D', 'status': 401, },
1479+ { 'path': '/repos/A/D/', 'status': 401, },
1480+ { 'path': '/repos/A/D/gamma', 'status': 401, },
1481+ { 'path': '/repos/A/D/H', 'status': 401, },
1482+ { 'path': '/repos/A/D/H/', 'status': 401, },
1483+ { 'path': '/repos/A/D/H/chi', 'status': 401, },
1484+ # auth is configured and user1 is allowed access repo including H
1485+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass},
1486+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass},
1487+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass},
1488+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass},
1489+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass},
1490+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass},
1491+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass},
1492+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1493+ 'user': user1, 'pw': user1_pass},
1494+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass},
1495+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass},
1496+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass},
1497+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass},
1498+ # try with the wrong password for user1
1499+ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass},
1500+ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1501+ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass},
1502+ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1503+ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass},
1504+ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1505+ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass},
1506+ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1507+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass},
1508+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass},
1509+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass},
1510+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass},
1511+ # auth is configured and user2 is not allowed access to H
1512+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass},
1513+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass},
1514+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass},
1515+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass},
1516+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass},
1517+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass},
1518+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass},
1519+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1520+ 'user': user2, 'pw': user2_pass},
1521+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass},
1522+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass},
1523+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass},
1524+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass},
1525+ # try with the wrong password for user2
1526+ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass},
1527+ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1528+ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass},
1529+ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1530+ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass},
1531+ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1532+ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass},
1533+ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1534+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass},
1535+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass},
1536+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass},
1537+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass},
1538+ # anon is allowed with the ALLOW header
1539+ { 'path': '', 'status': 301, 'headers': allow_header },
1540+ { 'path': '/', 'status': 200, 'headers': allow_header },
1541+ { 'path': '/repos', 'status': 301, 'headers': allow_header },
1542+ { 'path': '/repos/', 'status': 200, 'headers': allow_header },
1543+ { 'path': '/repos/A', 'status': 301, 'headers': allow_header },
1544+ { 'path': '/repos/A/', 'status': 200, 'headers': allow_header },
1545+ { 'path': '/repos/A/D', 'status': 301, 'headers': allow_header },
1546+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, 'headers': allow_header },
1547+ { 'path': '/repos/A/D/gamma', 'status': 200, 'headers': allow_header },
1548+ # these 3 tests return 403 instead of 401 becasue the config allows
1549+ # the anon user with the ALLOW header without any auth and the old hook
1550+ # system has no way of knowing it should return 401 since authentication is
1551+ # configured and can change the behavior. It could decide to return 401 just on
1552+ # the basis of authentication being configured but then that leaks info in other
1553+ # cases so it's better for this case to be "broken".
1554+ { 'path': '/repos/A/D/H', 'status': 403, 'headers': allow_header },
1555+ { 'path': '/repos/A/D/H/', 'status': 403, 'headers': allow_header },
1556+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'headers': allow_header },
1557+ # auth is configured and user1 is allowed access repo including H
1558+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1559+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1560+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1561+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1562+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1563+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1564+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1565+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1566+ 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1567+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1568+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1569+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1570+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1571+ # try with the wrong password for user1
1572+ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1573+ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1574+ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1575+ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1576+ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1577+ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1578+ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1579+ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1580+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1581+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1582+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1583+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1584+ # auth is configured and user2 is not allowed access to H
1585+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1586+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1587+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1588+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1589+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1590+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1591+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1592+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1593+ 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1594+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1595+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1596+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1597+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1598+ # try with the wrong password for user2
1599+ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1600+ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1601+ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1602+ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1603+ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1604+ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1605+ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1606+ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1607+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1608+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1609+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1610+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1611+
1612+ )
1613+
1614+ verify_gets(test_area_url, sallrany_tests)
1615+
1616+# See comments on authn_sallrany test for some background on the interaction
1617+# of Satisfy Any and the newer Require blocks.
1618+@SkipUnless(svntest.main.is_httpd_authz_provider_enabled)
1619+def authn_sallrall(sbox):
1620+ "test satisfy all require all config"
1621+ sbox.build(read_only = True, create_wc = False)
1622+
1623+ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos',
1624+ '/authz-test-work/sallrall')
1625+
1626+ write_authz_file(sbox)
1627+
1628+ allow_header = { 'ALLOW': '1' }
1629+
1630+ sallrall_tests = (
1631+ #anon access isn't allowed without ALLOW header
1632+ { 'path': '', 'status': 403, },
1633+ { 'path': '/', 'status': 403, },
1634+ { 'path': '/repos', 'status': 403, },
1635+ { 'path': '/repos/', 'status': 403, },
1636+ { 'path': '/repos/A', 'status': 403, },
1637+ { 'path': '/repos/A/', 'status': 403, },
1638+ { 'path': '/repos/A/D', 'status': 403, },
1639+ { 'path': '/repos/A/D/', 'status': 403, },
1640+ { 'path': '/repos/A/D/gamma', 'status': 403, },
1641+ { 'path': '/repos/A/D/H', 'status': 403, },
1642+ { 'path': '/repos/A/D/H/', 'status': 403, },
1643+ { 'path': '/repos/A/D/H/chi', 'status': 403, },
1644+ # auth is configured but no access is allowed without the ALLOW header
1645+ { 'path': '', 'status': 403, 'user': user1, 'pw': user1_pass},
1646+ { 'path': '/', 'status': 403, 'user': user1, 'pw': user1_pass},
1647+ { 'path': '/repos', 'status': 403, 'user': user1, 'pw': user1_pass},
1648+ { 'path': '/repos/', 'status': 403, 'user': user1, 'pw': user1_pass},
1649+ { 'path': '/repos/A', 'status': 403, 'user': user1, 'pw': user1_pass},
1650+ { 'path': '/repos/A/', 'status': 403, 'user': user1, 'pw': user1_pass},
1651+ { 'path': '/repos/A/D', 'status': 403, 'user': user1, 'pw': user1_pass},
1652+ { 'path': '/repos/A/D/', 'status': 403, 'user': user1, 'pw': user1_pass},
1653+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1, 'pw': user1_pass},
1654+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_pass},
1655+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_pass},
1656+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_pass},
1657+ # try with the wrong password for user1
1658+ { 'path': '', 'status': 403, 'user': user1, 'pw': user1_badpass},
1659+ { 'path': '/', 'status': 403, 'user': user1, 'pw': user1_badpass},
1660+ { 'path': '/repos', 'status': 403, 'user': user1, 'pw': user1_badpass},
1661+ { 'path': '/repos/', 'status': 403, 'user': user1, 'pw': user1_badpass},
1662+ { 'path': '/repos/A', 'status': 403, 'user': user1, 'pw': user1_badpass},
1663+ { 'path': '/repos/A/', 'status': 403, 'user': user1, 'pw': user1_badpass},
1664+ { 'path': '/repos/A/D', 'status': 403, 'user': user1, 'pw': user1_badpass},
1665+ { 'path': '/repos/A/D/', 'status': 403, 'user': user1, 'pw': user1_badpass},
1666+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1, 'pw': user1_badpass},
1667+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_badpass},
1668+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_badpass},
1669+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_badpass},
1670+ # auth is configured but no access is allowed without the ALLOW header
1671+ { 'path': '', 'status': 403, 'user': user2, 'pw': user2_pass},
1672+ { 'path': '/', 'status': 403, 'user': user2, 'pw': user2_pass},
1673+ { 'path': '/repos', 'status': 403, 'user': user2, 'pw': user2_pass},
1674+ { 'path': '/repos/', 'status': 403, 'user': user2, 'pw': user2_pass},
1675+ { 'path': '/repos/A', 'status': 403, 'user': user2, 'pw': user2_pass},
1676+ { 'path': '/repos/A/', 'status': 403, 'user': user2, 'pw': user2_pass},
1677+ { 'path': '/repos/A/D', 'status': 403, 'user': user2, 'pw': user2_pass},
1678+ { 'path': '/repos/A/D/', 'status': 403, 'user': user2, 'pw': user2_pass},
1679+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2, 'pw': user2_pass},
1680+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass},
1681+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass},
1682+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass},
1683+ # try with the wrong password for user2
1684+ { 'path': '', 'status': 403, 'user': user2, 'pw': user2_badpass},
1685+ { 'path': '/', 'status': 403, 'user': user2, 'pw': user2_badpass},
1686+ { 'path': '/repos', 'status': 403, 'user': user2, 'pw': user2_badpass},
1687+ { 'path': '/repos/', 'status': 403, 'user': user2, 'pw': user2_badpass},
1688+ { 'path': '/repos/A', 'status': 403, 'user': user2, 'pw': user2_badpass},
1689+ { 'path': '/repos/A/', 'status': 403, 'user': user2, 'pw': user2_badpass},
1690+ { 'path': '/repos/A/D', 'status': 403, 'user': user2, 'pw': user2_badpass},
1691+ { 'path': '/repos/A/D/', 'status': 403, 'user': user2, 'pw': user2_badpass},
1692+ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2, 'pw': user2_badpass},
1693+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_badpass},
1694+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_badpass},
1695+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_badpass},
1696+ # anon is not allowed even with ALLOW header
1697+ { 'path': '', 'status': 401, 'headers': allow_header },
1698+ { 'path': '/', 'status': 401, 'headers': allow_header },
1699+ { 'path': '/repos', 'status': 401, 'headers': allow_header },
1700+ { 'path': '/repos/', 'status': 401, 'headers': allow_header },
1701+ { 'path': '/repos/A', 'status': 401, 'headers': allow_header },
1702+ { 'path': '/repos/A/', 'status': 401, 'headers': allow_header },
1703+ { 'path': '/repos/A/D', 'status': 401, 'headers': allow_header },
1704+ { 'path': '/repos/A/D/', 'status': 401, 'headers': allow_header },
1705+ { 'path': '/repos/A/D/gamma', 'status': 401, 'headers': allow_header },
1706+ { 'path': '/repos/A/D/H', 'status': 401, 'headers': allow_header },
1707+ { 'path': '/repos/A/D/H/', 'status': 401, 'headers': allow_header },
1708+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'headers': allow_header },
1709+ # auth is configured and user1 is allowed access repo including H
1710+ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1711+ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1712+ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1713+ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1714+ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1715+ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1716+ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1717+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H,
1718+ 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1719+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1720+ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1721+ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1722+ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header },
1723+ # try with the wrong password for user1
1724+ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1725+ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1726+ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1727+ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1728+ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1729+ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1730+ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1731+ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1732+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1733+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1734+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1735+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header },
1736+ # auth is configured and user2 is not allowed access to H
1737+ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1738+ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1739+ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1740+ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1741+ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1742+ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1743+ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1744+ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H,
1745+ 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1746+ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1747+ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1748+ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1749+ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header },
1750+ # try with the wrong password for user2
1751+ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1752+ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1753+ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1754+ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1755+ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1756+ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1757+ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1758+ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1759+ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1760+ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1761+ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1762+ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header },
1763+
1764+ )
1765+
1766+ verify_gets(test_area_url, sallrall_tests)
1767+
1768+
1769+########################################################################
1770+# Run the tests
1771+
1772+
1773+# list all tests here, starting with None:
1774+test_list = [ None,
1775+ anon,
1776+ mixed,
1777+ mixed_noauthwhenanon,
1778+ authn,
1779+ authn_anonoff,
1780+ authn_lcuser,
1781+ authn_group,
1782+ authn_sallrany,
1783+ authn_sallrall,
1784+ ]
1785+serial_only = True
1786+
1787+if __name__ == '__main__':
1788+ svntest.main.run_tests(test_list)
1789+ # NOTREACHED
1790+
1791+
1792+### End of file.
1793
1794Property changes on: subversion/tests/cmdline/mod_authz_svn_tests.py
1795___________________________________________________________________
1796Added: svn:eol-style
1797## -0,0 +1 ##
1798+native
1799\ No newline at end of property
1800Index: subversion/tests/cmdline/svntest/main.py
1801===================================================================
1802--- a/subversion/tests/cmdline/svntest/main.py (revision 1691883)
1803+++ b/subversion/tests/cmdline/svntest/main.py (working copy)
1804@@ -1378,6 +1378,30 @@ def is_plaintext_password_storage_disabled():
1805 return False
1806 return True
1807
1808+
1809+# https://issues.apache.org/bugzilla/show_bug.cgi?id=56480
1810+# https://issues.apache.org/bugzilla/show_bug.cgi?id=55397
1811+__mod_dav_url_quoting_broken_versions = frozenset([
1812+ '2.2.27',
1813+ '2.2.26',
1814+ '2.2.25',
1815+ '2.4.9',
1816+ '2.4.8',
1817+ '2.4.7',
1818+ '2.4.6',
1819+ '2.4.5',
1820+])
1821+def is_mod_dav_url_quoting_broken():
1822+ if is_ra_type_dav():
1823+ return (options.httpd_version in __mod_dav_url_quoting_broken_versions)
1824+ return None
1825+
1826+def is_httpd_authz_provider_enabled():
1827+ if is_ra_type_dav():
1828+ v = options.httpd_version.split('.')
1829+ return (v[0] == '2' and int(v[1]) >= 3) or int(v[0]) > 2
1830+ return None
1831+
1832 ######################################################################
1833
1834
1835@@ -1435,6 +1459,8 @@ class TestSpawningThread(threading.Thread):
1836 args.append('--ssl-cert=' + options.ssl_cert)
1837 if options.http_proxy:
1838 args.append('--http-proxy=' + options.http_proxy)
1839+ if options.httpd_version:
1840+ args.append('--httpd-version=' + options.httpd_version)
1841
1842 result, stdout_lines, stderr_lines = spawn_process(command, 0, False, None,
1843 *args)
1844@@ -1600,6 +1626,12 @@ class TestRunner:
1845 sandbox.cleanup_test_paths()
1846 return exit_code
1847
1848+def is_httpd_authz_provider_enabled():
1849+ if is_ra_type_dav():
1850+ v = options.httpd_version.split('.')
1851+ return (v[0] == '2' and int(v[1]) >= 3) or int(v[0]) > 2
1852+ return None
1853+
1854 ######################################################################
1855 # Main testing functions
1856
1857@@ -1780,6 +1812,8 @@ def _create_parser():
1858 help='Path to SSL server certificate.')
1859 parser.add_option('--http-proxy', action='store',
1860 help='Use the HTTP Proxy at hostname:port.')
1861+ parser.add_option('--httpd-version', action='store',
1862+ help='Assume HTTPD is this version.')
1863 parser.add_option('--tools-bin', action='store', dest='tools_bin',
1864 help='Use the svn tools installed in this path')
1865
1866Index: win-tests.py
1867===================================================================
1868--- a/win-tests.py (revision 1691883)
1869+++ b/win-tests.py (working copy)
1870@@ -481,6 +481,7 @@ class Httpd:
1871 self.httpd_config = os.path.join(self.root, 'httpd.conf')
1872 self.httpd_users = os.path.join(self.root, 'users')
1873 self.httpd_mime_types = os.path.join(self.root, 'mime.types')
1874+ self.httpd_groups = os.path.join(self.root, 'groups')
1875 self.abs_builddir = abs_builddir
1876 self.abs_objdir = abs_objdir
1877 self.service_name = 'svn-test-httpd-' + str(httpd_port)
1878@@ -494,6 +495,7 @@ class Httpd:
1879 create_target_dir(self.root_dir)
1880
1881 self._create_users_file()
1882+ self._create_groups_file()
1883 self._create_mime_types_file()
1884 self._create_dontdothat_file()
1885
1886@@ -540,6 +542,8 @@ class Httpd:
1887 if self.httpd_ver >= 2.2:
1888 fp.write(self._sys_module('auth_basic_module', 'mod_auth_basic.so'))
1889 fp.write(self._sys_module('authn_file_module', 'mod_authn_file.so'))
1890+ fp.write(self._sys_module('authz_groupfile_module', 'mod_authz_groupfile.so'))
1891+ fp.write(self._sys_module('authz_host_module', 'mod_authz_host.so'))
1892 else:
1893 fp.write(self._sys_module('auth_module', 'mod_auth.so'))
1894 fp.write(self._sys_module('alias_module', 'mod_alias.so'))
1895@@ -562,6 +566,7 @@ class Httpd:
1896 # Define two locations for repositories
1897 fp.write(self._svn_repo('repositories'))
1898 fp.write(self._svn_repo('local_tmp'))
1899+ fp.write(self._svn_authz_repo())
1900
1901 # And two redirects for the redirect tests
1902 fp.write('RedirectMatch permanent ^/svn-test-work/repositories/'
1903@@ -592,7 +597,18 @@ class Httpd:
1904 'jrandom', 'rayjandom'])
1905 os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
1906 'jconstant', 'rayjandom'])
1907+ os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
1908+ 'JRANDOM', 'rayjandom'])
1909+ os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users,
1910+ 'JCONSTANT', 'rayjandom'])
1911
1912+ def _create_groups_file(self):
1913+ "Create groups for mod_authz_svn tests"
1914+ fp = open(self.httpd_groups, 'w')
1915+ fp.write('random: jrandom\n')
1916+ fp.write('constant: jconstant\n')
1917+ fp.close()
1918+
1919 def _create_mime_types_file(self):
1920 "Create empty mime.types file"
1921 fp = open(self.httpd_mime_types, 'w')
1922@@ -652,6 +668,153 @@ class Httpd:
1923 ' DontDoThatConfigFile ' + self._quote(self.dontdothat_file) + '\n' \
1924 '</Location>\n'
1925
1926+ def _svn_authz_repo(self):
1927+ local_tmp = os.path.join(self.abs_builddir,
1928+ CMDLINE_TEST_SCRIPT_NATIVE_PATH,
1929+ 'svn-test-work', 'local_tmp')
1930+ return \
1931+ '<Location /authz-test-work/anon>' + '\n' \
1932+ ' DAV svn' + '\n' \
1933+ ' SVNParentPath ' + local_tmp + '\n' \
1934+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
1935+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
1936+ ' SVNListParentPath On' + '\n' \
1937+ ' <IfModule mod_authz_core.c>' + '\n' \
1938+ ' Require all granted' + '\n' \
1939+ ' </IfModule>' + '\n' \
1940+ ' <IfModule !mod_authz_core.c>' + '\n' \
1941+ ' Allow from all' + '\n' \
1942+ ' </IfModule>' + '\n' \
1943+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
1944+ '</Location>' + '\n' \
1945+ '<Location /authz-test-work/mixed>' + '\n' \
1946+ ' DAV svn' + '\n' \
1947+ ' SVNParentPath ' + local_tmp + '\n' \
1948+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
1949+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
1950+ ' SVNListParentPath On' + '\n' \
1951+ ' AuthType Basic' + '\n' \
1952+ ' AuthName "Subversion Repository"' + '\n' \
1953+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
1954+ ' Require valid-user' + '\n' \
1955+ ' Satisfy Any' + '\n' \
1956+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
1957+ '</Location>' + '\n' \
1958+ '<Location /authz-test-work/mixed-noauthwhenanon>' + '\n' \
1959+ ' DAV svn' + '\n' \
1960+ ' SVNParentPath ' + local_tmp + '\n' \
1961+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
1962+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
1963+ ' SVNListParentPath On' + '\n' \
1964+ ' AuthType Basic' + '\n' \
1965+ ' AuthName "Subversion Repository"' + '\n' \
1966+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
1967+ ' Require valid-user' + '\n' \
1968+ ' AuthzSVNNoAuthWhenAnonymousAllowed On' + '\n' \
1969+ ' SVNPathAuthz On' + '\n' \
1970+ '</Location>' + '\n' \
1971+ '<Location /authz-test-work/authn>' + '\n' \
1972+ ' DAV svn' + '\n' \
1973+ ' SVNParentPath ' + local_tmp + '\n' \
1974+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
1975+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
1976+ ' SVNListParentPath On' + '\n' \
1977+ ' AuthType Basic' + '\n' \
1978+ ' AuthName "Subversion Repository"' + '\n' \
1979+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
1980+ ' Require valid-user' + '\n' \
1981+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
1982+ '</Location>' + '\n' \
1983+ '<Location /authz-test-work/authn-anonoff>' + '\n' \
1984+ ' DAV svn' + '\n' \
1985+ ' SVNParentPath ' + local_tmp + '\n' \
1986+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
1987+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
1988+ ' SVNListParentPath On' + '\n' \
1989+ ' AuthType Basic' + '\n' \
1990+ ' AuthName "Subversion Repository"' + '\n' \
1991+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
1992+ ' Require valid-user' + '\n' \
1993+ ' AuthzSVNAnonymous Off' + '\n' \
1994+ ' SVNPathAuthz On' + '\n' \
1995+ '</Location>' + '\n' \
1996+ '<Location /authz-test-work/authn-lcuser>' + '\n' \
1997+ ' DAV svn' + '\n' \
1998+ ' SVNParentPath ' + local_tmp + '\n' \
1999+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
2000+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
2001+ ' SVNListParentPath On' + '\n' \
2002+ ' AuthType Basic' + '\n' \
2003+ ' AuthName "Subversion Repository"' + '\n' \
2004+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
2005+ ' Require valid-user' + '\n' \
2006+ ' AuthzForceUsernameCase Lower' + '\n' \
2007+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
2008+ '</Location>' + '\n' \
2009+ '<Location /authz-test-work/authn-lcuser>' + '\n' \
2010+ ' DAV svn' + '\n' \
2011+ ' SVNParentPath ' + local_tmp + '\n' \
2012+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
2013+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
2014+ ' SVNListParentPath On' + '\n' \
2015+ ' AuthType Basic' + '\n' \
2016+ ' AuthName "Subversion Repository"' + '\n' \
2017+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
2018+ ' Require valid-user' + '\n' \
2019+ ' AuthzForceUsernameCase Lower' + '\n' \
2020+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
2021+ '</Location>' + '\n' \
2022+ '<Location /authz-test-work/authn-group>' + '\n' \
2023+ ' DAV svn' + '\n' \
2024+ ' SVNParentPath ' + local_tmp + '\n' \
2025+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
2026+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
2027+ ' SVNListParentPath On' + '\n' \
2028+ ' AuthType Basic' + '\n' \
2029+ ' AuthName "Subversion Repository"' + '\n' \
2030+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
2031+ ' AuthGroupFile ' + self._quote(self.httpd_groups) + '\n' \
2032+ ' Require group random' + '\n' \
2033+ ' AuthzSVNAuthoritative Off' + '\n' \
2034+ ' SVNPathAuthz On' + '\n' \
2035+ '</Location>' + '\n' \
2036+ '<IfModule mod_authz_core.c>' + '\n' \
2037+ '<Location /authz-test-work/sallrany>' + '\n' \
2038+ ' DAV svn' + '\n' \
2039+ ' SVNParentPath ' + local_tmp + '\n' \
2040+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
2041+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
2042+ ' SVNListParentPath On' + '\n' \
2043+ ' AuthType Basic' + '\n' \
2044+ ' AuthName "Subversion Repository"' + '\n' \
2045+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
2046+ ' AuthzSendForbiddenOnFailure On' + '\n' \
2047+ ' Satisfy All' + '\n' \
2048+ ' <RequireAny>' + '\n' \
2049+ ' Require valid-user' + '\n' \
2050+ ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \
2051+ ' </RequireAny>' + '\n' \
2052+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
2053+ '</Location>' + '\n' \
2054+ '<Location /authz-test-work/sallrall>'+ '\n' \
2055+ ' DAV svn' + '\n' \
2056+ ' SVNParentPath ' + local_tmp + '\n' \
2057+ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \
2058+ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \
2059+ ' SVNListParentPath On' + '\n' \
2060+ ' AuthType Basic' + '\n' \
2061+ ' AuthName "Subversion Repository"' + '\n' \
2062+ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \
2063+ ' AuthzSendForbiddenOnFailure On' + '\n' \
2064+ ' Satisfy All' + '\n' \
2065+ ' <RequireAll>' + '\n' \
2066+ ' Require valid-user' + '\n' \
2067+ ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \
2068+ ' </RequireAll>' + '\n' \
2069+ ' SVNPathAuthz ' + self.path_authz_option + '\n' \
2070+ '</Location>' + '\n' \
2071+ '</IfModule>' + '\n' \
2072+
2073 def start(self):
2074 if self.service:
2075 self._start_service()
2076@@ -786,6 +949,10 @@ if not test_javahl:
2077 log_file = os.path.join(abs_builddir, log)
2078 fail_log_file = os.path.join(abs_builddir, faillog)
2079
2080+ if run_httpd:
2081+ httpd_version = "%.1f" % daemon.httpd_ver
2082+ else:
2083+ httpd_version = None
2084 th = run_tests.TestHarness(abs_srcdir, abs_builddir,
2085 log_file,
2086 fail_log_file,
2087@@ -795,6 +962,7 @@ if not test_javahl:
2088 fsfs_sharding, fsfs_packing,
2089 list_tests, svn_bin, mode_filter,
2090 milestone_filter,
2091+ httpd_version=httpd_version,
2092 set_log_level=log_level, ssl_cert=ssl_cert)
2093 old_cwd = os.getcwd()
2094 try:
diff --git a/meta/recipes-devtools/subversion/subversion_1.8.13.bb b/meta/recipes-devtools/subversion/subversion_1.8.13.bb
index 9c9bdb192b..9505247be5 100644
--- a/meta/recipes-devtools/subversion/subversion_1.8.13.bb
+++ b/meta/recipes-devtools/subversion/subversion_1.8.13.bb
@@ -14,6 +14,7 @@ SRC_URI = "${APACHE_MIRROR}/${BPN}/${BPN}-${PV}.tar.bz2 \
14 file://libtool2.patch \ 14 file://libtool2.patch \
15 file://disable_macos.patch \ 15 file://disable_macos.patch \
16 file://serf.m4-Regex-modified-to-allow-D-in-paths.patch \ 16 file://serf.m4-Regex-modified-to-allow-D-in-paths.patch \
17 file://subversion-CVE-2015-3184.patch \
17" 18"
18SRC_URI[md5sum] = "4413417b529d7bdf82f74e50df02e88b" 19SRC_URI[md5sum] = "4413417b529d7bdf82f74e50df02e88b"
19SRC_URI[sha256sum] = "1099cc68840753b48aedb3a27ebd1e2afbcc84ddb871412e5d500e843d607579" 20SRC_URI[sha256sum] = "1099cc68840753b48aedb3a27ebd1e2afbcc84ddb871412e5d500e843d607579"