Merge branch 'stable-2.14' into stable-2.15

* stable-2.14:
  Bazel: Include eclipse-out directory in .bazelignore

Change-Id: Id1b9ea70d47a368e36b12e4bd49aedd3f605ae2c
diff --git a/WORKSPACE b/WORKSPACE
index dfa2ec1..208224a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "714a32382ebd02919007d3514513af4395768d80",
+    commit = "b54eaed487d37188120da6933b97c571519954ca",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/DefaultAccessRights.java b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/DefaultAccessRights.java
index 17dedb6..4df4f25 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/DefaultAccessRights.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/DefaultAccessRights.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.RefPattern;
@@ -34,6 +35,7 @@
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.Optional;
 import java.util.Set;
 import java.util.regex.Pattern;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -143,7 +145,10 @@
   private String getOwnerGroupName(ProjectState project) {
     Set<AccountGroup.UUID> owners = project.getAllOwners();
     if (!owners.isEmpty()) {
-      return groupCache.get(owners.iterator().next()).getName();
+      Optional<InternalGroup> owner = groupCache.get(owners.iterator().next());
+      if (owner.isPresent()) {
+        return owner.get().getName();
+      }
     }
     return String.format("no owners for project %s", project.getProject().getName());
   }
@@ -183,13 +188,14 @@
         // this means that group is not already in the groups file, so
         // we need to check if group exist if if it does, get its
         // uuid.
-        AccountGroup group = groupCache.get(new AccountGroup.NameKey(rule.getGroup().getName()));
+        Optional<InternalGroup> group =
+            groupCache.get(new AccountGroup.NameKey(rule.getGroup().getName()));
 
-        if (group == null) {
+        if (!group.isPresent()) {
           log.error("Group {} not found", rule.getGroup().getName());
           continue;
         }
-        rule.getGroup().setUUID(group.getGroupUUID());
+        rule.getGroup().setUUID(group.get().getGroupUUID());
       }
       perm.add(rule);
     }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java
index 278987c..a3fbef5 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidator.java
@@ -21,15 +21,20 @@
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.api.groups.GroupInput;
 import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.GroupMembership;
 import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.group.CreateGroup;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.CreateProjectArgs;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectControl;
@@ -37,8 +42,10 @@
 import com.google.gerrit.server.validators.ValidationException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -82,21 +89,30 @@
   private final CreateGroup.Factory createGroupFactory;
   private final String documentationUrl;
   private final AllProjectsNameProvider allProjectsName;
+  private final Provider<CurrentUser> self;
+  private final PermissionBackend permissionBackend;
   private final PluginConfigFactory cfg;
   private final String pluginName;
+  private final ProjectControl.GenericFactory projectControlFactory;
 
   @Inject
   public ProjectCreationValidator(
       CreateGroup.Factory createGroupFactory,
       @PluginCanonicalWebUrl String url,
       AllProjectsNameProvider allProjectsName,
+      Provider<CurrentUser> self,
+      PermissionBackend permissionBackend,
       PluginConfigFactory cfg,
-      @PluginName String pluginName) {
+      @PluginName String pluginName,
+      ProjectControl.GenericFactory projectControlFactory) {
     this.createGroupFactory = createGroupFactory;
     this.documentationUrl = url + "Documentation/index.html";
     this.allProjectsName = allProjectsName;
+    this.self = self;
+    this.permissionBackend = permissionBackend;
     this.cfg = cfg;
     this.pluginName = pluginName;
+    this.projectControlFactory = projectControlFactory;
   }
 
   @Override
@@ -108,14 +124,30 @@
           String.format(PROJECT_CANNOT_CONTAINS_SPACES_MSG, documentationUrl));
     }
 
-    ProjectControl parentCtrl = args.newParent;
-    if (parentCtrl.getUser().getCapabilities().canAdministrateServer()) {
+    ProjectControl parentCtrl;
+    try {
+      parentCtrl = projectControlFactory.controlFor(args.newParent, self.get());
+    } catch (NoSuchProjectException | IOException e) {
+      log.error(
+          "Failed to create project {}; Cannot retrieve info about parent project {}: {}",
+          name,
+          args.newParent.get(),
+          e.getMessage(),
+          e);
+      throw new ValidationException(AN_ERROR_OCCURRED_MSG);
+    }
+
+    try {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+
       // Admins can bypass any rules to support creating projects that doesn't
       // comply with the new naming rules. New projects structures have to
       // comply but we need to be able to add new project to an existing non
       // compliant structure.
       log.debug("admin is creating project, bypassing all rules");
       return;
+    } catch (AuthException | PermissionBackendException e) {
+      // continuing
     }
 
     if (allProjectsName.get().equals(parentCtrl.getProject().getNameKey())) {
@@ -171,7 +203,7 @@
                 .apply(TopLevelResource.INSTANCE, new GroupInput());
       }
       return AccountGroup.UUID.parse(groupInfo.id);
-    } catch (RestApiException | OrmException | IOException e) {
+    } catch (RestApiException | OrmException | IOException | ConfigInvalidException e) {
       log.error("Failed to create project {}: {}", name, e.getMessage(), e);
       throw new ValidationException(AN_ERROR_OCCURRED_MSG);
     }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java b/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java
index 8ea7dc6..8006b23 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/projectgroupstructure/ProjectCreationValidatorIT.java
@@ -162,7 +162,8 @@
     ProjectState projectState = projectCache.get(new Project.NameKey(rootProject));
     assertThat(projectState.getOwners().size()).isEqualTo(1);
     assertThat(projectState.getOwners())
-        .contains(groupCache.get(new AccountGroup.NameKey(rootProject + "-admins")).getGroupUUID());
+        .contains(
+            groupCache.get(new AccountGroup.NameKey(rootProject + "-admins")).get().getGroupUUID());
 
     // case when <project-name>-admins group already exists
     rootProject = name("rootProject2");
@@ -179,7 +180,8 @@
                 .toString()
                 .substring(0, 7);
     assertThat(projectState.getOwners())
-        .contains(groupCache.get(new AccountGroup.NameKey(expectedOwnerGroup)).getGroupUUID());
+        .contains(
+            groupCache.get(new AccountGroup.NameKey(expectedOwnerGroup)).get().getGroupUUID());
   }
 
   @Test
@@ -250,7 +252,10 @@
     assertThat(projectState.getOwners().size()).isEqualTo(1);
     assertThat(projectState.getOwners())
         .contains(
-            groupCache.get(new AccountGroup.NameKey(childProject + "-admins")).getGroupUUID());
+            groupCache
+                .get(new AccountGroup.NameKey(childProject + "-admins"))
+                .get()
+                .getGroupUUID());
 
     // case when <project-name>-admins group already exists
     String childProject2 = parent + "/childProject2";
@@ -267,7 +272,8 @@
                 .toString()
                 .substring(0, 7);
     assertThat(projectState.getOwners())
-        .contains(groupCache.get(new AccountGroup.NameKey(expectedOwnerGroup)).getGroupUUID());
+        .contains(
+            groupCache.get(new AccountGroup.NameKey(expectedOwnerGroup)).get().getGroupUUID());
   }
 
   @Test