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