Merge "Assign default permissions on user branches"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index b2d6a90..7991afe2 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -42,6 +42,7 @@
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.extensions.api.accounts.EmailInput;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
+import com.google.gerrit.extensions.api.changes.ReviewInput;
 import com.google.gerrit.extensions.api.changes.StarsInput;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.common.AccountInfo;
@@ -381,6 +382,13 @@
         cloneProject(allUsers, user);
     String userRefName = RefNames.refsUsers(user.id);
 
+    // remove default READ permissions
+    ProjectConfig cfg = projectCache.checkedGet(allUsers).getConfig();
+    cfg.getAccessSection(
+        RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true)
+        .remove(new Permission(Permission.READ));
+    saveProjectConfig(allUsers, cfg);
+
     // deny READ permission that is inherited from All-Projects
     deny(allUsers, Permission.READ, ANONYMOUS_USERS, RefNames.REFS + "*");
 
@@ -427,15 +435,11 @@
         GeneralPreferencesInfo.defaults().changesPerPage + 10;
     gApi.accounts().self().setPreferences(input);
 
-    removeExclusiveReadPermissionOnAllUsers();
-    String userRefName = RefNames.refsUsers(admin.id);
-    grant(Permission.PUSH, allUsers, userRefName);
-
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
     fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
     allUsersRepo.reset("userRef");
     PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
-    push.to(userRefName).assertOkStatus();
+    push.to(RefNames.refsUsers(admin.id)).assertOkStatus();
 
     push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
     push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
@@ -450,29 +454,23 @@
         GeneralPreferencesInfo.defaults().changesPerPage + 10;
     gApi.accounts().self().setPreferences(input);
 
-    removeExclusiveReadPermissionOnAllUsers();
     String userRefName = RefNames.refsUsers(admin.id);
-    grant(Permission.PUSH, allUsers, MagicBranch.NEW_CHANGE + userRefName);
-
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+    fetch(allUsersRepo, userRefName + ":userRef");
     allUsersRepo.reset("userRef");
     PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
     PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName);
     r.assertOkStatus();
     assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
+    gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+    gApi.changes().id(r.getChangeId()).current().submit();
 
     push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
     r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF);
     r.assertOkStatus();
     assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
-  }
-
-  private void removeExclusiveReadPermissionOnAllUsers() throws Exception {
-    ProjectConfig cfg = projectCache.checkedGet(allUsers).getConfig();
-    cfg.getAccessSection(RefNames.REFS_USERS + "*", true)
-        .remove(new Permission(Permission.READ));
-    saveProjectConfig(allUsers, cfg);
+    gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
+    gApi.changes().id(r.getChangeId()).current().submit();
   }
 
   @Test
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AclUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AclUtil.java
index ced8cd0..2f49f9e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AclUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AclUtil.java
@@ -29,7 +29,16 @@
 
   public static void grant(ProjectConfig config, AccessSection section,
       String permission, boolean force, GroupReference... groupList) {
+    grant(config, section, permission, force, null, groupList);
+  }
+
+  public static void grant(ProjectConfig config, AccessSection section,
+      String permission, boolean force, Boolean exclusive,
+      GroupReference... groupList) {
     Permission p = section.getPermission(permission, true);
+    if (exclusive != null) {
+      p.setExclusiveGroup(exclusive);
+    }
     for (GroupReference group : groupList) {
       if (group != null) {
         PermissionRule r = rule(config, group);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java
index 79849fb..b697519 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -14,11 +14,13 @@
 
 package com.google.gerrit.server.schema;
 
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
 import static com.google.gerrit.server.schema.AclUtil.grant;
 
 import com.google.gerrit.common.Version;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
@@ -28,6 +30,8 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.project.RefPattern;
 import com.google.inject.Inject;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -42,6 +46,7 @@
   private final GitRepositoryManager mgr;
   private final AllUsersName allUsersName;
   private final PersonIdent serverUser;
+  private final GroupReference registered;
 
   private GroupReference admin;
 
@@ -53,6 +58,7 @@
     this.mgr = mgr;
     this.allUsersName = allUsersName;
     this.serverUser = serverUser;
+    this.registered = SystemGroupBackend.getGroup(REGISTERED_USERS);
   }
 
   public AllUsersCreator setAdministrators(GroupReference admin) {
@@ -87,8 +93,13 @@
       Project project = config.getProject();
       project.setDescription("Individual user settings and preferences.");
 
-      AccessSection all = config.getAccessSection(RefNames.REFS_USERS + "*", true);
-      all.getPermission(Permission.READ, true).setExclusiveGroup(true);
+      AccessSection users = config.getAccessSection(
+          RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true);
+      LabelType cr = AllProjectsCreator.initCodeReviewLabel(config);
+      grant(config, users, Permission.READ, false, true, registered);
+      grant(config, users, Permission.PUSH, false, true, registered);
+      grant(config, users, Permission.SUBMIT, false, true, registered);
+      grant(config, users, cr, -2, 2, registered);
 
       AccessSection defaults = config.getAccessSection(RefNames.REFS_USERS_DEFAULT, true);
       defaults.getPermission(Permission.READ, true).setExclusiveGroup(true);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index ddffd36..6373f9e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -33,7 +33,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  public static final Class<Schema_124> C = Schema_124.class;
+  public static final Class<Schema_125> C = Schema_125.class;
 
   public static int getBinaryVersion() {
     return guessVersion(C);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_125.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_125.java
new file mode 100644
index 0000000..90b28a7
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_125.java
@@ -0,0 +1,131 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.server.schema.AclUtil.grant;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.project.RefPattern;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Schema_125 extends SchemaVersion {
+  private static final String COMMIT_MSG =
+      "Assign default permissions on user branches\n" +
+      "\n" +
+      "By default each user should be able to read and update the own user\n" +
+      "branch. Also the user should be able to approve and submit changes for\n" +
+      "the own user branch. Assign default permissions for this and remove the\n" +
+      "old exclusive read protection from the user branches.\n";
+
+  private final GitRepositoryManager repoManager;
+  private final AllUsersName allUsersName;
+  private final AllProjectsName allProjectsName;
+  private final PersonIdent serverUser;
+
+  @Inject
+  Schema_125(Provider<Schema_124> prior,
+      GitRepositoryManager repoManager,
+      AllUsersName allUsersName,
+      AllProjectsName allProjectsName,
+      @GerritPersonIdent PersonIdent serverUser) {
+    super(prior);
+    this.repoManager = repoManager;
+    this.allUsersName = allUsersName;
+    this.allProjectsName = allProjectsName;
+    this.serverUser = serverUser;
+  }
+
+  @Override
+  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
+    try (Repository git = repoManager.openRepository(allUsersName);
+        MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED,
+            allUsersName, git)) {
+      ProjectConfig config = ProjectConfig.read(md);
+      config.load(md);
+
+      config.getAccessSection(RefNames.REFS_USERS + "*", true)
+          .remove(new Permission(Permission.READ));
+      GroupReference registered = SystemGroupBackend.getGroup(REGISTERED_USERS);
+      AccessSection users = config.getAccessSection(
+          RefNames.REFS_USERS + "${" + RefPattern.USERID_SHARDED + "}", true);
+      grant(config, users, Permission.READ, true, registered);
+      grant(config, users, Permission.PUSH, true, registered);
+      grant(config, users, Permission.SUBMIT, true, registered);
+
+      for (LabelType lt : getLabelTypes(config)) {
+        if ("Code-Review".equals(lt.getName())
+            || "Verified".equals(lt.getName())) {
+          grant(config, users, lt, lt.getMin().getValue(),
+              lt.getMax().getValue(), registered);
+        }
+      }
+
+      md.getCommitBuilder().setAuthor(serverUser);
+      md.getCommitBuilder().setCommitter(serverUser);
+      md.setMessage(COMMIT_MSG);
+      config.commit(md);
+    } catch (ConfigInvalidException | IOException ex) {
+      throw new OrmException(ex);
+    }
+  }
+
+  private Collection<LabelType> getLabelTypes(ProjectConfig config)
+      throws IOException, ConfigInvalidException {
+    Map<String, LabelType> labelTypes =
+        new HashMap<>(config.getLabelSections());
+    Project.NameKey parent = config.getProject().getParent(allProjectsName);
+    while (parent != null) {
+      try (Repository git = repoManager.openRepository(parent);
+          MetaDataUpdate md =
+              new MetaDataUpdate(GitReferenceUpdated.DISABLED, parent, git)) {
+        ProjectConfig parentConfig = ProjectConfig.read(md);
+        parentConfig.load(md);
+        for (LabelType lt : parentConfig.getLabelSections().values()) {
+          if (!labelTypes.containsKey(lt.getName())) {
+            labelTypes.put(lt.getName(), lt);
+          }
+        }
+        parent = parentConfig.getProject().getParent(allProjectsName);
+      }
+    }
+    return labelTypes.values();
+  }
+}