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