Fix issue #4762 "authz doesn't combine global and repository rules"

The new authz implementation of SVN 1.10 introduced an incompatible
change in the interpretation of authz rules:

Global access rules can be configured for a particular user and a specific
path. Such global rules were ignored if a repository-specific rule is also
present for the same path, even if this repository-specific rule does not
apply to the user in question.

This change seems unnecessary because it broke backwards compatibility with
existing authz files, from SVN 1.9 and older, for no discernible benefit.
The change was probably not intentional as this situation was not covered
by the test suite before a test case was added in r1835049.

Restore the behaviour of SVN 1.9: It is now again possible to override
per-path access rules for specific users (and groups) at the global level.
Such global rules are overridden by repository-specific rules only if
both the user and the path match the repository-specific rule.

* subversion/libsvn_repos/authz.c
  (create_user_authz): Prefer rules which apply to both the user and
    the path over rules which apply only to the path. If both a global
    and a repository-specific rule apply to user and path then prefer
    the repository-specific one.

* subversion/tests/libsvn_repos/authz-test.c
  (reposful_reposless_stanzas_inherit): Remove XFAIL marker.


git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1882326 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/libsvn_repos/authz.c b/subversion/libsvn_repos/authz.c
index 9f8dbc5..0a47a09 100644
--- a/subversion/libsvn_repos/authz.c
+++ b/subversion/libsvn_repos/authz.c
@@ -889,9 +889,7 @@
   /* Use a separate sub-pool to keep memory usage tight. */
   apr_pool_t *subpool = svn_pool_create(scratch_pool);
 
-  /* Find all ACLs for REPOSITORY.
-   * Note that repo-specific rules replace global rules,
-   * even if they don't apply to the current user. */
+  /* Find all ACLs for REPOSITORY. */
   apr_array_header_t *acls = apr_array_make(subpool, authz->acls->nelts,
                                             sizeof(authz_acl_t *));
   for (i = 0; i < authz->acls->nelts; ++i)
@@ -908,15 +906,36 @@
                 = APR_ARRAY_IDX(acls, acls->nelts - 1, const authz_acl_t *);
               if (svn_authz__compare_paths(&prev_acl->rule, &acl->rule) == 0)
                 {
+                  svn_boolean_t global_acl_applies;
+                  svn_boolean_t repos_acl_applies;
+
+                  /* Previous ACL is a global rule. */
                   SVN_ERR_ASSERT_NO_RETURN(!strcmp(prev_acl->rule.repos,
                                                    AUTHZ_ANY_REPOSITORY));
+                  /* Current ACL is a per-repository rule. */
                   SVN_ERR_ASSERT_NO_RETURN(strcmp(acl->rule.repos,
                                                   AUTHZ_ANY_REPOSITORY));
-                  apr_array_pop(acls);
-                }
-            }
 
-          APR_ARRAY_PUSH(acls, const authz_acl_t *) = acl;
+                  global_acl_applies =
+                    svn_authz__get_acl_access(NULL, prev_acl, user, repository);
+                  repos_acl_applies =
+                    svn_authz__get_acl_access(NULL, acl, user, repository);
+
+                  /* Prefer rules which apply to both this user and this path
+                   * over rules which apply only to the path. In cases where
+                   * both rules apply to user and path, always prefer the
+                   * repository-specific rule. */
+                  if (!global_acl_applies || repos_acl_applies)
+                    {
+                      apr_array_pop(acls);
+                      APR_ARRAY_PUSH(acls, const authz_acl_t *) = acl;
+                    }
+                }
+              else
+                APR_ARRAY_PUSH(acls, const authz_acl_t *) = acl;
+            }
+          else
+            APR_ARRAY_PUSH(acls, const authz_acl_t *) = acl;
         }
     }
 
diff --git a/subversion/tests/libsvn_repos/authz-test.c b/subversion/tests/libsvn_repos/authz-test.c
index 33e9323..b48a880 100644
--- a/subversion/tests/libsvn_repos/authz-test.c
+++ b/subversion/tests/libsvn_repos/authz-test.c
@@ -522,7 +522,7 @@
                    "test svn_authz__get_global_rights"),
     SVN_TEST_PASS2(issue_4741_groups,
                    "issue 4741 groups"),
-    SVN_TEST_XFAIL2(reposful_reposless_stanzas_inherit,
+    SVN_TEST_PASS2(reposful_reposless_stanzas_inherit,
                     "[foo:/] inherits [/]"),
     SVN_TEST_NULL
   };