[GERRITHUB-4] Set Gerrit refs/meta/config on imported repos.

Create initial Project properties (project.config
and groups) on refs/meta/config whenever a GitHub
project gets imported.

In order to allow a non-Gerrit administrator to have
full control over the imported project, assign
extra owner and GitHub-like permissions to the individual
user  that performed the operation.

P.S. In order to allow Gerrit to assign permissions
to an individual user, a SingleUserGroup plugin is
needed to be installed.

Change-Id: I596b2e80b4d9519668a1ab289d6c950139d6a922
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/RepositoriesCloneController.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/RepositoriesCloneController.java
index 6272cb2..7987001 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/RepositoriesCloneController.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/RepositoriesCloneController.java
@@ -63,7 +63,8 @@
       String repository =
           req.getParameter(REPO_PARAM_PREFIX + repoIdx + "_repository");
       try {
-        gitCloner.clone(repoIdx, organisation, repository);
+        gitCloner.clone(repoIdx, organisation, repository,
+            getDescription(hubLogin, organisation, repository));
       } catch (Exception e) {
         errorMgr.submit(e);
       }
@@ -71,4 +72,14 @@
     }
   }
 
+  private String getDescription(GitHubLogin hubLogin, String organisation,
+      String repository) throws IOException {
+    if (organisation.equals(hubLogin.getMyself().getLogin())) {
+      return hubLogin.getMyself().getRepository(repository).getDescription();
+    } else {
+      return hubLogin.hub.getOrganization(organisation)
+          .getRepository(repository).getDescription();
+    }
+  }
+
 }
diff --git a/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitClone.java b/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitClone.java
index 7dcdf43..49f2aaa 100644
--- a/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitClone.java
+++ b/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitClone.java
@@ -15,13 +15,33 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Set;
 
 import org.apache.commons.io.FileUtils;
 import org.eclipse.jgit.api.CloneCommand;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
+import com.google.gerrit.reviewdb.client.Project.NameKey;
+import com.google.gerrit.reviewdb.client.Project.SubmitType;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.MetaDataUpdate.User;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.ProjectCache;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -29,30 +49,54 @@
   private static final String GITHUB_REPOSITORY_FORMAT =
       "https://github.com/%1$s/%2$s.git";
   private static final Logger log = LoggerFactory.getLogger(GitCloner.class);
+
+  private static final String CODE_REVIEW_REFS = "refs/for/refs/*";
+  private static final String TAGS_REFS = "refs/tags/*";
+  private static final String CODE_REVIEW_LABEL = "Code-Review";
+  private static final String VERIFIED_LABEL = "Verified";
+
   private final String organisation;
   private final String repository;
 
   private final File gitDir;
   private String sourceUri;
   private File destinationDirectory;
+  private User metaDataUpdateFactory;
+  private String description;
+  private GroupBackend groupBackend;
+  private String username;
+  private ProjectConfig config;
+  private ProjectCache projectCache;
 
   public interface Factory {
     GitClone create(@Assisted("organisation") String organisation,
-        @Assisted("name") String repository);
+        @Assisted("name") String repository,
+        @Assisted("description") String description,
+        @Assisted("username") String username);
   }
 
   @Inject
   public GitClone(GitConfig gitConfig,
+      MetaDataUpdate.User metaDataUpdateFactory, 
+      GroupBackend groupBackend,
+      ProjectCache projectCache,
       @Assisted("organisation") String organisation,
-      @Assisted("name") String repository)
+      @Assisted("name") String repository,
+      @Assisted("description") String description,
+      @Assisted("username") String username)
       throws GitDestinationAlreadyExistsException,
       GitDestinationNotWritableException {
     this.gitDir = gitConfig.gitDir;
     this.organisation = organisation;
     this.repository = repository;
+    this.description = description;
     this.sourceUri = getSourceUri(organisation, repository);
     this.destinationDirectory =
         getDestinationDirectory(organisation, repository);
+    this.metaDataUpdateFactory = metaDataUpdateFactory;
+    this.groupBackend = groupBackend;
+    this.projectCache = projectCache;
+    this.username = username;
   }
 
   public void doClone(ProgressMonitor progress) throws GitCloneFailedException,
@@ -72,6 +116,99 @@
     }
   }
 
+  public void configureProject(ProgressMonitor progress)
+      throws RepositoryNotFoundException, IOException, ConfigInvalidException {
+    MetaDataUpdate md = metaDataUpdateFactory.create(getProjectNameKey());
+    try {
+      config = ProjectConfig.read(md);
+      progress.beginTask("Configure Gerrit project", 2);
+      setProjectSettings();
+      progress.update(1);
+      setProjectPermissions();
+      progress.update(1);
+      md.setMessage("Imported from " + sourceUri);
+      config.commit(md);
+      projectCache.onCreateProject(getProjectNameKey());
+    } finally {
+      md.close();
+      progress.endTask();
+    }
+  }
+
+  private void setProjectPermissions() {
+    addPermissions(AccessSection.ALL, Permission.OWNER);
+
+    addPermissions(CODE_REVIEW_REFS, Permission.READ, Permission.PUSH,
+        Permission.REMOVE_REVIEWER, Permission.SUBMIT, Permission.REBASE);
+
+    PermissionRule reviewRange = new PermissionRule(getMyGroup());
+    reviewRange.setMin(-2);
+    reviewRange.setMax(+2);
+    addPermission(CODE_REVIEW_REFS, Permission.LABEL + CODE_REVIEW_LABEL,
+        reviewRange);
+
+    PermissionRule verifiedRange = new PermissionRule(getMyGroup());
+    verifiedRange.setMin(-1);
+    verifiedRange.setMax(+1);
+    addPermission(CODE_REVIEW_REFS, Permission.LABEL + VERIFIED_LABEL,
+        verifiedRange);
+
+    addPermissions(AccessSection.HEADS, Permission.READ, Permission.CREATE,
+        Permission.PUSH_MERGE);
+
+    PermissionRule forcePush = new PermissionRule(getMyGroup());
+    forcePush.setForce(true);
+    addPermission(AccessSection.HEADS, Permission.PUSH, forcePush);
+
+    addPermissions(TAGS_REFS, Permission.PUSH_TAG, Permission.PUSH_SIGNED_TAG);
+
+    PermissionRule removeTag = new PermissionRule(getMyGroup());
+    removeTag.setForce(true);
+    addPermission(TAGS_REFS, Permission.PUSH, removeTag);
+  }
+
+  private void setProjectSettings() {
+    Project project = config.getProject();
+    project.setDescription(description);
+    project.setSubmitType(SubmitType.MERGE_IF_NECESSARY);
+    project.setUseContributorAgreements(InheritableBoolean.INHERIT);
+    project.setUseSignedOffBy(InheritableBoolean.INHERIT);
+    project.setUseContentMerge(InheritableBoolean.INHERIT);
+    project.setRequireChangeID(InheritableBoolean.INHERIT);
+  }
+
+  private void addPermissions(String refSpec, String... permissions) {
+    AccessSection accessSection = config.getAccessSection(refSpec, true);
+    for (String permission : permissions) {
+      String[] permParts = permission.split("=");
+      String action = permParts[0];
+      PermissionRule rule;
+      if (permParts.length > 1) {
+        rule = PermissionRule.fromString(permParts[1], true);
+        rule.setGroup(getMyGroup());
+      } else {
+        rule = new PermissionRule(getMyGroup());
+      }
+      accessSection.getPermission(action, true).add(rule);
+    }
+  }
+
+  private void addPermission(String refSpec, String action, PermissionRule rule) {
+    config.getAccessSection(refSpec, true).getPermission(action, true)
+        .add(rule);
+  }
+
+
+  private GroupReference getMyGroup() {
+    GroupDescription.Basic g =
+        groupBackend.get(AccountGroup.UUID.parse("user:" + username));
+    return config.resolve(GroupReference.forGroup(g));
+  }
+
+  private NameKey getProjectNameKey() {
+    return Project.NameKey.parse(organisation + "/" + repository);
+  }
+
   private File getDestinationDirectory(String organisation, String repository)
       throws GitDestinationAlreadyExistsException,
       GitDestinationNotWritableException {
diff --git a/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloneJob.java b/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloneJob.java
index 183203c..11d3e8a 100644
--- a/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloneJob.java
+++ b/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloneJob.java
@@ -39,6 +39,7 @@
   public void run() {
     try {
       cloneCommand.doClone(this);
+      cloneCommand.configureProject(this);
       status = GitCloneStatus.COMPLETE;
     } catch (Exception e) {
       if (status == GitCloneStatus.SYNC) {
diff --git a/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloner.java b/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloner.java
index 685b693..d9ff5f9 100644
--- a/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloner.java
+++ b/github-plugin/src/main/java/com/googlesrouce/gerrit/plugins/github/git/GitCloner.java
@@ -19,6 +19,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.gerrit.server.IdentifiedUser;
 import com.google.inject.Inject;
 import com.google.inject.servlet.SessionScoped;
 import com.googlesrouce.gerrit.plugins.github.git.GitClone.Factory;
@@ -30,18 +31,20 @@
   private final ConcurrentHashMap<Integer, CloneJob> cloneJobs =
       new ConcurrentHashMap<Integer, CloneJob>();
   private final GitCommandsExecutor executor;
+  private IdentifiedUser user;
 
 
   @Inject
-  public GitCloner(GitClone.Factory cloneFactory, GitCommandsExecutor executor) {
+  public GitCloner(GitClone.Factory cloneFactory, GitCommandsExecutor executor, IdentifiedUser user) {
     this.cloneFactory = cloneFactory;
     this.executor = executor;
+    this.user = user;
   }
 
-  public void clone(int idx, String organisation, String repository) {
+  public void clone(int idx, String organisation, String repository, String description) {
     try {
       GitCloneJob gitCloneJob =
-          new GitCloneJob(idx, cloneFactory.create(organisation, repository));
+          new GitCloneJob(idx, cloneFactory.create(organisation, repository, description, user.getUserName()));
       log.debug("New Git clone job created: " + gitCloneJob);
       executor.exec(gitCloneJob);
       cloneJobs.put(idx, gitCloneJob);
diff --git a/github-plugin/src/test/java/com/googlesource/gerrit/plugins/github/wizard/test/GitClonerTest.java b/github-plugin/src/test/java/com/googlesource/gerrit/plugins/github/wizard/test/GitClonerTest.java
deleted file mode 100644
index 54971aa..0000000
--- a/github-plugin/src/test/java/com/googlesource/gerrit/plugins/github/wizard/test/GitClonerTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.googlesource.gerrit.plugins.github.wizard.test;
-
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.commons.io.FileUtils;
-import org.jukito.JukitoRunner;
-import org.jukito.TestModule;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.SitePath;
-import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.github.GuiceModule;
-import com.googlesrouce.gerrit.plugins.github.git.GitClone;
-import com.googlesrouce.gerrit.plugins.github.git.GitCloneFailedException;
-import com.googlesrouce.gerrit.plugins.github.git.GitConfig;
-import com.googlesrouce.gerrit.plugins.github.git.GitDestinationAlreadyExistsException;
-import com.googlesrouce.gerrit.plugins.github.git.GitDestinationNotWritableException;
-
-@RunWith(JukitoRunner.class)
-public class GitClonerTest {
-  private static final String GERRIT_SITE_PATH = System.getProperty(
-      "gerrit.site", "/tmp/gerrit-site-test");
-  private static final String TEST_ORGANISATION = "lucamilanesio";
-  private static final String TEST_REPOSITORY = "33degree";
-  public static final File GIT_BASE_PATH = new File(GERRIT_SITE_PATH, "git");
-
-  public static class GitClonerTestModule extends TestModule {
-    @Override
-    protected void configureTest() {
-      bind(GitConfig.class).toInstance(new GitConfig(GIT_BASE_PATH));
-      bind(File.class).annotatedWith(SitePath.class).toInstance(
-          new File(GERRIT_SITE_PATH));
-      bind(String.class).annotatedWith(PluginName.class).toInstance("TEST");
-      install(new GuiceModule());
-    }
-  }
-
-  @Inject
-  private GitClone.Factory cloner;
-
-  @Before
-  public void setUp() throws IOException {
-    if (GIT_BASE_PATH.exists()) {
-      FileUtils.deleteDirectory(GIT_BASE_PATH);
-    }
-  }
-
-  @Test
-  public void testShouldCloneBareGitHubRepositoryCreateTargetDirectoryWithGitRepository()
-      throws GitDestinationAlreadyExistsException, GitCloneFailedException,
-      GitDestinationNotWritableException {
-    cloner.create(TEST_ORGANISATION, TEST_REPOSITORY).doClone(null);
-
-    File destDirectory =
-        new File(new File(GIT_BASE_PATH, TEST_ORGANISATION), TEST_REPOSITORY
-            + ".git");
-    assertTrue(destDirectory.exists());
-    assertTrue(new File(destDirectory, "HEAD").exists());
-  }
-
-}