Merge "Resource exhausted because of unclosed LDAP connection" into stable-2.10
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
index 63d2b35..321c2ea 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PermissionCollection.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.project;
 
+import static com.google.common.base.Objects.firstNonNull;
 import static com.google.gerrit.server.project.RefControl.isRE;
 
 import com.google.common.collect.Lists;
@@ -113,6 +114,7 @@
       Set<String> exclusiveGroupPermissions = new HashSet<>();
 
       HashMap<String, List<PermissionRule>> permissions = new HashMap<>();
+      HashMap<String, List<PermissionRule>> overridden = new HashMap<>();
       Map<PermissionRule, ProjectRef> ruleProps = Maps.newIdentityHashMap();
       for (AccessSection section : sections) {
         Project.NameKey project = sectionToProject.get(section);
@@ -128,11 +130,19 @@
             } else {
               addRule = seen.add(s) && !rule.isDeny() && !exclusivePermissionExists;
             }
+
+            HashMap<String, List<PermissionRule>> p = null;
             if (addRule) {
-              List<PermissionRule> r = permissions.get(permission.getName());
+              p = permissions;
+            } else if (!rule.isDeny() && !exclusivePermissionExists) {
+              p = overridden;
+            }
+
+            if (p != null) {
+              List<PermissionRule> r = p.get(permission.getName());
               if (r == null) {
                 r = new ArrayList<>(2);
-                permissions.put(permission.getName(), r);
+                p.put(permission.getName(), r);
               }
               r.add(rule);
               ruleProps.put(rule, new ProjectRef(project, section.getName()));
@@ -145,18 +155,22 @@
         }
       }
 
-      return new PermissionCollection(permissions, ruleProps, perUser);
+      return new PermissionCollection(permissions, overridden, ruleProps,
+          perUser);
     }
   }
 
   private final Map<String, List<PermissionRule>> rules;
+  private final Map<String, List<PermissionRule>> overridden;
   private final Map<PermissionRule, ProjectRef> ruleProps;
   private final boolean perUser;
 
   private PermissionCollection(Map<String, List<PermissionRule>> rules,
+      Map<String, List<PermissionRule>> overridden,
       Map<PermissionRule, ProjectRef> ruleProps,
       boolean perUser) {
     this.rules = rules;
+    this.overridden = overridden;
     this.ruleProps = ruleProps;
     this.perUser = perUser;
   }
@@ -182,6 +196,11 @@
     return r != null ? r : Collections.<PermissionRule> emptyList();
   }
 
+  List<PermissionRule> getOverridden(String permissionName) {
+    return firstNonNull(
+        overridden.get(permissionName), Collections.<PermissionRule> emptyList());
+  }
+
   ProjectRef getRuleProps(PermissionRule rule) {
     return ruleProps.get(rule);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index 00ecee3..7dc7a2a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -120,6 +120,7 @@
    */
   public boolean isVisibleByRegisteredUsers() {
     List<PermissionRule> access = relevant.getPermission(Permission.READ);
+    List<PermissionRule> overridden = relevant.getOverridden(Permission.READ);
     Set<ProjectRef> allows = Sets.newHashSet();
     Set<ProjectRef> blocks = Sets.newHashSet();
     for (PermissionRule rule : access) {
@@ -129,6 +130,11 @@
         allows.add(relevant.getRuleProps(rule));
       }
     }
+    for (PermissionRule rule : overridden) {
+      if (SystemGroupBackend.isAnonymousOrRegistered(rule.getGroup())) {
+        blocks.remove(relevant.getRuleProps(rule));
+      }
+    }
     blocks.removeAll(allows);
     return blocks.isEmpty() && !allows.isEmpty();
   }
@@ -492,6 +498,7 @@
 
   private boolean doCanPerform(String permissionName, boolean blockOnly) {
     List<PermissionRule> access = access(permissionName);
+    List<PermissionRule> overridden = relevant.getOverridden(permissionName);
     Set<ProjectRef> allows = Sets.newHashSet();
     Set<ProjectRef> blocks = Sets.newHashSet();
     for (PermissionRule rule : access) {
@@ -501,6 +508,9 @@
         allows.add(relevant.getRuleProps(rule));
       }
     }
+    for (PermissionRule rule : overridden) {
+      blocks.remove(relevant.getRuleProps(rule));
+    }
     blocks.removeAll(allows);
     return blocks.isEmpty() && (!allows.isEmpty() || blockOnly);
   }
@@ -508,6 +518,7 @@
   /** True if the user has force this permission. Works only for non labels. */
   private boolean canForcePerform(String permissionName) {
     List<PermissionRule> access = access(permissionName);
+    List<PermissionRule> overridden = relevant.getOverridden(permissionName);
     Set<ProjectRef> allows = Sets.newHashSet();
     Set<ProjectRef> blocks = Sets.newHashSet();
     for (PermissionRule rule : access) {
@@ -517,6 +528,11 @@
         allows.add(relevant.getRuleProps(rule));
       }
     }
+    for (PermissionRule rule : overridden) {
+      if (rule.getForce()) {
+        blocks.remove(relevant.getRuleProps(rule));
+      }
+    }
     blocks.removeAll(allows);
     return blocks.isEmpty() && !allows.isEmpty();
   }
@@ -524,6 +540,7 @@
   /** True if for this permission force is blocked for the user. Works only for non labels. */
   private boolean isForceBlocked(String permissionName) {
     List<PermissionRule> access = access(permissionName);
+    List<PermissionRule> overridden = relevant.getOverridden(permissionName);
     Set<ProjectRef> allows = Sets.newHashSet();
     Set<ProjectRef> blocks = Sets.newHashSet();
     for (PermissionRule rule : access) {
@@ -533,6 +550,11 @@
         allows.add(relevant.getRuleProps(rule));
       }
     }
+    for (PermissionRule rule : overridden) {
+      if (rule.getForce()) {
+        blocks.remove(relevant.getRuleProps(rule));
+      }
+    }
     blocks.removeAll(allows);
     return !blocks.isEmpty();
   }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index dc3529c..5478a6c 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -55,6 +55,8 @@
   private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers");
   private Project.NameKey localKey = new Project.NameKey("local");
   private ProjectConfig local;
+  private Project.NameKey parentKey = new Project.NameKey("parent");
+  private ProjectConfig parent;
   private final Util util;
 
   public RefControlTest() {
@@ -63,9 +65,14 @@
 
   @Before
   public void setUp() throws Exception {
+    parent = new ProjectConfig(parentKey);
+    parent.load(newRepository(parentKey));
+    util.add(parent);
+
     local = new ProjectConfig(localKey);
     local.load(newRepository(localKey));
     util.add(local);
+    local.getProject().setParentName(parentKey);
   }
 
   @Test
@@ -139,8 +146,8 @@
 
   @Test
   public void testInheritRead_SingleBranchDeniesUpload() {
-    allow(util.getParentConfig(), READ, REGISTERED_USERS, "refs/*");
-    allow(util.getParentConfig(), PUSH, REGISTERED_USERS, "refs/for/refs/*");
+    allow(parent, READ, REGISTERED_USERS, "refs/*");
+    allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
     allow(local, READ, REGISTERED_USERS, "refs/heads/foobar");
     doNotInherit(local, READ, "refs/heads/foobar");
     doNotInherit(local, PUSH, "refs/for/refs/heads/foobar");
@@ -157,8 +164,8 @@
 
   @Test
   public void testBlockPushDrafts() {
-    allow(util.getParentConfig(), PUSH, REGISTERED_USERS, "refs/for/refs/*");
-    block(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/drafts/*");
+    allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
+    block(parent, PUSH, ANONYMOUS_USERS, "refs/drafts/*");
 
     ProjectControl u = util.user(local);
     assertTrue("can upload refs/heads/master",
@@ -169,8 +176,8 @@
 
   @Test
   public void testBlockPushDraftsUnblockAdmin() {
-    block(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/drafts/*");
-    allow(util.getParentConfig(), PUSH, ADMIN, "refs/drafts/*");
+    block(parent, PUSH, ANONYMOUS_USERS, "refs/drafts/*");
+    allow(parent, PUSH, ADMIN, "refs/drafts/*");
 
     assertTrue("push is blocked for anonymous to refs/drafts/master",
         util.user(local).controlForRef("refs/drafts/refs/heads/master")
@@ -182,8 +189,8 @@
 
   @Test
   public void testInheritRead_SingleBranchDoesNotOverrideInherited() {
-    allow(util.getParentConfig(), READ, REGISTERED_USERS, "refs/*");
-    allow(util.getParentConfig(), PUSH, REGISTERED_USERS, "refs/for/refs/*");
+    allow(parent, READ, REGISTERED_USERS, "refs/*");
+    allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
     allow(local, READ, REGISTERED_USERS, "refs/heads/foobar");
 
     ProjectControl u = util.user(local);
@@ -198,20 +205,20 @@
 
   @Test
   public void testInheritDuplicateSections() throws Exception {
-    allow(util.getParentConfig(), READ, ADMIN, "refs/*");
+    allow(parent, READ, ADMIN, "refs/*");
     allow(local, READ, DEVS, "refs/heads/*");
-    local.getProject().setParentName(util.getParentConfig().getProject().getName());
     assertTrue("a can read", util.user(local, "a", ADMIN).isVisible());
 
-    local = new ProjectConfig(new Project.NameKey("local"));
+    local = new ProjectConfig(localKey);
     local.load(newRepository(localKey));
+    local.getProject().setParentName(parentKey);
     allow(local, READ, DEVS, "refs/*");
     assertTrue("d can read", util.user(local, "d", DEVS).isVisible());
   }
 
   @Test
   public void testInheritRead_OverrideWithDeny() {
-    allow(util.getParentConfig(), READ, REGISTERED_USERS, "refs/*");
+    allow(parent, READ, REGISTERED_USERS, "refs/*");
     deny(local, READ, REGISTERED_USERS, "refs/*");
 
     ProjectControl u = util.user(local);
@@ -220,7 +227,7 @@
 
   @Test
   public void testInheritRead_AppendWithDenyOfRef() {
-    allow(util.getParentConfig(), READ, REGISTERED_USERS, "refs/*");
+    allow(parent, READ, REGISTERED_USERS, "refs/*");
     deny(local, READ, REGISTERED_USERS, "refs/heads/*");
 
     ProjectControl u = util.user(local);
@@ -232,7 +239,7 @@
 
   @Test
   public void testInheritRead_OverridesAndDeniesOfRef() {
-    allow(util.getParentConfig(), READ, REGISTERED_USERS, "refs/*");
+    allow(parent, READ, REGISTERED_USERS, "refs/*");
     deny(local, READ, REGISTERED_USERS, "refs/*");
     allow(local, READ, REGISTERED_USERS, "refs/heads/*");
 
@@ -245,7 +252,7 @@
 
   @Test
   public void testInheritSubmit_OverridesAndDeniesOfRef() {
-    allow(util.getParentConfig(), SUBMIT, REGISTERED_USERS, "refs/*");
+    allow(parent, SUBMIT, REGISTERED_USERS, "refs/*");
     deny(local, SUBMIT, REGISTERED_USERS, "refs/*");
     allow(local, SUBMIT, REGISTERED_USERS, "refs/heads/*");
 
@@ -257,7 +264,7 @@
 
   @Test
   public void testCannotUploadToAnyRef() {
-    allow(util.getParentConfig(), READ, REGISTERED_USERS, "refs/*");
+    allow(parent, READ, REGISTERED_USERS, "refs/*");
     allow(local, READ, DEVS, "refs/heads/*");
     allow(local, PUSH, DEVS, "refs/for/refs/heads/*");
 
@@ -307,7 +314,7 @@
   @Test
   public void testSortWithRegex() {
     allow(local, READ, DEVS, "^refs/heads/.*");
-    allow(util.getParentConfig(), READ, ANONYMOUS_USERS, "^refs/heads/.*-QA-.*");
+    allow(parent, READ, ANONYMOUS_USERS, "^refs/heads/.*-QA-.*");
 
     ProjectControl u = util.user(local, DEVS), d = util.user(local, DEVS);
     assertTrue("u can read", u.controlForRef("refs/heads/foo-QA-bar").isVisible());
@@ -317,7 +324,7 @@
   @Test
   public void testBlockRule_ParentBlocksChild() {
     allow(local, PUSH, DEVS, "refs/tags/*");
-    block(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/tags/*");
+    block(parent, PUSH, ANONYMOUS_USERS, "refs/tags/*");
     ProjectControl u = util.user(local, DEVS);
     assertFalse("u can't update tag", u.controlForRef("refs/tags/V10").canUpdate());
   }
@@ -326,7 +333,7 @@
   public void testBlockRule_ParentBlocksChildEvenIfAlreadyBlockedInChild() {
     allow(local, PUSH, DEVS, "refs/tags/*");
     block(local, PUSH, ANONYMOUS_USERS, "refs/tags/*");
-    block(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/tags/*");
+    block(parent, PUSH, ANONYMOUS_USERS, "refs/tags/*");
 
     ProjectControl u = util.user(local, DEVS);
     assertFalse("u can't update tag", u.controlForRef("refs/tags/V10").canUpdate());
@@ -335,7 +342,7 @@
   @Test
   public void testBlockLabelRange_ParentBlocksChild() {
     allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
-    block(util.getParentConfig(), LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
+    block(parent, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
 
     ProjectControl u = util.user(local, DEVS);
 
@@ -350,7 +357,7 @@
   public void testBlockLabelRange_ParentBlocksChildEvenIfAlreadyBlockedInChild() {
     allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
     block(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
-    block(util.getParentConfig(), LABEL + "Code-Review", -2, +2, DEVS,
+    block(parent, LABEL + "Code-Review", -2, +2, DEVS,
         "refs/heads/*");
 
     ProjectControl u = util.user(local, DEVS);
@@ -364,6 +371,17 @@
   }
 
   @Test
+  public void testInheritSubmit_AllowInChildDoesntAffectUnblockInParent() {
+    block(parent, SUBMIT, ANONYMOUS_USERS, "refs/heads/*");
+    allow(parent, SUBMIT, REGISTERED_USERS, "refs/heads/*");
+    allow(local, SUBMIT, REGISTERED_USERS, "refs/heads/*");
+
+    ProjectControl u = util.user(local);
+    assertFalse("not blocked from submitting", u.controlForRef(
+        "refs/heads/master").isBlocked(SUBMIT));
+  }
+
+  @Test
   public void testUnblockNoForce() {
     block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
     allow(local, PUSH, DEVS, "refs/heads/*");
@@ -412,7 +430,7 @@
 
   @Test
   public void testUnblockInLocal_Fails() {
-    block(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/heads/*");
+    block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
     allow(local, PUSH, fixers, "refs/heads/*");
 
     ProjectControl f = util.user(local, fixers);
@@ -421,8 +439,8 @@
 
   @Test
   public void testUnblockInParentBlockInLocal() {
-    block(util.getParentConfig(), PUSH, ANONYMOUS_USERS, "refs/heads/*");
-    allow(util.getParentConfig(), PUSH, DEVS, "refs/heads/*");
+    block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
+    allow(parent, PUSH, DEVS, "refs/heads/*");
     block(local, PUSH, DEVS, "refs/heads/*");
 
     ProjectControl d = util.user(local, DEVS);
@@ -440,7 +458,7 @@
 
   @Test
   public void testUnblockInLocalVisibilityByRegisteredUsers_Fails() {
-    block(util.getParentConfig(), READ, ANONYMOUS_USERS, "refs/heads/*");
+    block(parent, READ, ANONYMOUS_USERS, "refs/heads/*");
     allow(local, READ, REGISTERED_USERS, "refs/heads/*");
 
     ProjectControl u = util.user(local, REGISTERED_USERS);
@@ -459,7 +477,7 @@
 
   @Test
   public void testUnblockInLocalForceEditTopicName_Fails() {
-    block(util.getParentConfig(), EDIT_TOPIC_NAME, ANONYMOUS_USERS, "refs/heads/*");
+    block(parent, EDIT_TOPIC_NAME, ANONYMOUS_USERS, "refs/heads/*");
     allow(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
 
     ProjectControl u = util.user(local, REGISTERED_USERS);
@@ -502,7 +520,7 @@
 
   @Test
   public void testUnblockInLocalRange_Fails() {
-    block(util.getParentConfig(), LABEL + "Code-Review", -1, 1, ANONYMOUS_USERS,
+    block(parent, LABEL + "Code-Review", -1, 1, ANONYMOUS_USERS,
         "refs/heads/*");
     allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
index c0f35ba..ac1e0d7 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
@@ -180,17 +180,19 @@
   private final PermissionCollection.Factory sectionSorter;
   private final GitRepositoryManager repoManager;
 
-  private final AllProjectsName allProjectsName = new AllProjectsName("parent");
-  private final ProjectConfig parent = new ProjectConfig(allProjectsName);
+  private final AllProjectsName allProjectsName =
+      new AllProjectsName("All-Projects");
+  private final ProjectConfig allProjects;
 
   public Util() {
     all = new HashMap<>();
     repoManager = new InMemoryRepositoryManager();
     try {
       Repository repo = repoManager.createRepository(allProjectsName);
-      parent.load(repo);
-      parent.getLabelSections().put(CR.getName(), CR);
-      add(parent);
+      allProjects = new ProjectConfig(new Project.NameKey(allProjectsName.get()));
+      allProjects.load(repo);
+      allProjects.getLabelSections().put(CR.getName(), CR);
+      add(allProjects);
     } catch (IOException | ConfigInvalidException e) {
       throw new RuntimeException(e);
     }
@@ -281,10 +283,6 @@
       injector.getInstance(ChangeControl.AssistedFactory.class);
   }
 
-  public ProjectConfig getParentConfig() {
-    return this.parent;
-  }
-
   public void add(ProjectConfig pc) {
     PrologEnvironment.Factory envFactory = null;
     ProjectControl.AssistedFactory projectControlFactory = null;