summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWenzong Fan <wenzong.fan@windriver.com>2016-02-06 23:14:48 (GMT)
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-02-07 17:23:04 (GMT)
commitf0ecaf46bb8e2a1bc0f22ee8650d10cbcc746a73 (patch)
tree343f92ccac68ee853cce06ed50d5cf6c4d75dce7
parent165fa6ce6213ab2b9610732a4926496b78ca4038 (diff)
downloadpoky-f0ecaf46bb8e2a1bc0f22ee8650d10cbcc746a73.tar.gz
subversion: fix CVE-2015-3184
mod_authz_svn in Apache Subversion 1.7.x before 1.7.21 and 1.8.x before 1.8.14, when using Apache httpd 2.4.x, does not properly restrict anonymous access, which allows remote anonymous users to read hidden files via the path name. Patch is from: http://subversion.apache.org/security/CVE-2015-3184-advisory.txt (From OE-Core master rev: 29eb921ed074d86fa8d5b205a313eb3177473a63) (From OE-Core rev: 7af7a3e692a6cd0d92768024efe32bfa7d83bc8f) (From OE-Core rev: e4a1caecc5ae6b8488ec8ed7d303296af99146c0) Signed-off-by: Wenzong Fan <wenzong.fan@windriver.com> Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> Signed-off-by: Robert Yang <liezhi.yang@windriver.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> Signed-off-by: Armin Kuster <akuster@mvista.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/recipes-devtools/subversion/subversion-1.8.11/subversion-CVE-2015-3184.patch2082
-rw-r--r--meta/recipes-devtools/subversion/subversion_1.8.11.bb1
2 files changed, 2083 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 0000000..2daa7c7
--- /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:
diff --git a/meta/recipes-devtools/subversion/subversion_1.8.11.bb b/meta/recipes-devtools/subversion/subversion_1.8.11.bb
index a5a5761..7893929 100644
--- a/meta/recipes-devtools/subversion/subversion_1.8.11.bb
+++ b/meta/recipes-devtools/subversion/subversion_1.8.11.bb
@@ -12,6 +12,7 @@ inherit gettext pythonnative
12SRC_URI = "${APACHE_MIRROR}/${BPN}/${BPN}-${PV}.tar.bz2 \ 12SRC_URI = "${APACHE_MIRROR}/${BPN}/${BPN}-${PV}.tar.bz2 \
13 file://libtool2.patch \ 13 file://libtool2.patch \
14 file://disable_macos.patch \ 14 file://disable_macos.patch \
15 file://subversion-CVE-2015-3184.patch \
15" 16"
16SRC_URI[md5sum] = "766a89bbbb388f8eb76166672d3b9e49" 17SRC_URI[md5sum] = "766a89bbbb388f8eb76166672d3b9e49"
17SRC_URI[sha256sum] = "10b056420e1f194c12840368f6bf58842e6200f9cb8cc5ebbf9be2e89e56e4d9" 18SRC_URI[sha256sum] = "10b056420e1f194c12840368f6bf58842e6200f9cb8cc5ebbf9be2e89e56e4d9"