Add capability that allows project owners to delete the owned projects
If the 'Delete Own Project' capability is granted project owners can
delete the projects that they own, but not any other projects.
Change-Id: I0406f5ddef5ce8a0cf17233b59e07269bd1863ef
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java
index f260acc..a928dd1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java
@@ -14,10 +14,13 @@
package com.googlesource.gerrit.plugins.deleteproject;
+import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.googlesource.gerrit.plugins.deleteproject.cache.CacheDeleteHandler;
import com.googlesource.gerrit.plugins.deleteproject.database.DatabaseDeleteHandler;
import com.googlesource.gerrit.plugins.deleteproject.fs.FilesystemDeleteHandler;
@@ -28,8 +31,11 @@
DeleteAction(AllProjectsNameProvider allProjectsNameProvider,
DatabaseDeleteHandler dbHandler,
FilesystemDeleteHandler fsHandler,
- CacheDeleteHandler cacheHandler) {
- super(allProjectsNameProvider, dbHandler, fsHandler, cacheHandler);
+ CacheDeleteHandler cacheHandler,
+ Provider<CurrentUser> userProvider,
+ @PluginName String pluginName) {
+ super(allProjectsNameProvider, dbHandler, fsHandler, cacheHandler,
+ userProvider, pluginName);
}
@Override
@@ -40,6 +46,7 @@
? String.format("No deletion of %s project",
allProjectsName)
: String.format("Delete project %s", rsrc.getName()))
- .setEnabled(!isAllProjects(rsrc));
+ .setEnabled(!isAllProjects(rsrc))
+ .setVisible(canDelete(rsrc));
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java
index 4a5482f..a712a7f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java
@@ -14,6 +14,9 @@
package com.googlesource.gerrit.plugins.deleteproject;
+import static com.googlesource.gerrit.plugins.deleteproject.DeleteOwnProjectCapability.DELETE_OWN_PROJECT;
+import static com.googlesource.gerrit.plugins.deleteproject.DeleteProjectCapability.DELETE_PROJECT;
+
import java.io.IOException;
import java.util.Collection;
@@ -22,8 +25,10 @@
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -33,12 +38,12 @@
import com.google.gerrit.sshd.SshCommand;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.googlesource.gerrit.plugins.deleteproject.cache.CacheDeleteHandler;
import com.googlesource.gerrit.plugins.deleteproject.database.CannotDeleteProjectException;
import com.googlesource.gerrit.plugins.deleteproject.database.DatabaseDeleteHandler;
import com.googlesource.gerrit.plugins.deleteproject.fs.FilesystemDeleteHandler;
-@RequiresCapability(DeleteProjectCapability.DELETE_PROJECT)
@CommandMetaData(name = "delete", description = "Delete specific project")
public final class DeleteCommand extends SshCommand {
@Argument(index = 0, required = true, metaVar = "NAME", usage = "project to delete")
@@ -58,6 +63,8 @@
private final CacheDeleteHandler cacheDeleteHandler;
private final DatabaseDeleteHandler databaseDeleteHandler;
private final FilesystemDeleteHandler filesystemDeleteHandler;
+ private final Provider<CurrentUser> userProvider;
+ private final String pluginName;
@Inject
protected DeleteCommand(SitePaths site,
@@ -65,16 +72,28 @@
AllProjectsNameProvider allProjectsNameProvider,
DatabaseDeleteHandler databaseDeleteHandler,
FilesystemDeleteHandler filesystemDeleteHandler,
- CacheDeleteHandler cacheDeleteHandler) {
+ CacheDeleteHandler cacheDeleteHandler,
+ Provider<CurrentUser> userProvider,
+ @PluginName String pluginName) {
this.site = site;
this.allProjectsName = allProjectsNameProvider.get();
this.databaseDeleteHandler = databaseDeleteHandler;
this.filesystemDeleteHandler = filesystemDeleteHandler;
this.cacheDeleteHandler = cacheDeleteHandler;
+ this.userProvider = userProvider;
+ this.pluginName = pluginName;
}
@Override
public void run() throws Failure {
+ CapabilityControl ctl = userProvider.get().getCapabilities();
+ if (!ctl.canAdministrateServer()
+ && !ctl.canPerform(pluginName + "-" + DELETE_PROJECT)
+ && !(ctl.canPerform(pluginName + "-" + DELETE_OWN_PROJECT)
+ && projectControl.isOwner())) {
+ throw new UnloggedFailure("not allowed to delete project");
+ }
+
final Project project = projectControl.getProject();
final String projectName = project.getName();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteOwnProjectCapability.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteOwnProjectCapability.java
new file mode 100644
index 0000000..2759915
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteOwnProjectCapability.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2014 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.googlesource.gerrit.plugins.deleteproject;
+
+import com.google.gerrit.extensions.config.CapabilityDefinition;
+
+public class DeleteOwnProjectCapability extends CapabilityDefinition {
+ static final String DELETE_OWN_PROJECT = "deleteOwnProject";
+
+ @Override
+ public String getDescription() {
+ return "Delete Own Project";
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
index c500a47..1b2ee46 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
@@ -14,30 +14,36 @@
package com.googlesource.gerrit.plugins.deleteproject;
+import static com.googlesource.gerrit.plugins.deleteproject.DeleteOwnProjectCapability.DELETE_OWN_PROJECT;
+import static com.googlesource.gerrit.plugins.deleteproject.DeleteProjectCapability.DELETE_PROJECT;
+
import java.io.IOException;
import java.util.Collection;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.googlesource.gerrit.plugins.deleteproject.DeleteProject.Input;
import com.googlesource.gerrit.plugins.deleteproject.cache.CacheDeleteHandler;
import com.googlesource.gerrit.plugins.deleteproject.database.CannotDeleteProjectException;
import com.googlesource.gerrit.plugins.deleteproject.database.DatabaseDeleteHandler;
import com.googlesource.gerrit.plugins.deleteproject.fs.FilesystemDeleteHandler;
-@RequiresCapability(DeleteProjectCapability.DELETE_PROJECT)
class DeleteProject implements RestModifyView<ProjectResource, Input> {
static class Input {
boolean preserve;
@@ -48,22 +54,32 @@
private final DatabaseDeleteHandler dbHandler;
private final FilesystemDeleteHandler fsHandler;
private final CacheDeleteHandler cacheHandler;
+ private final Provider<CurrentUser> userProvider;
+ private final String pluginName;
@Inject
DeleteProject(AllProjectsNameProvider allProjectsNameProvider,
DatabaseDeleteHandler dbHandler,
FilesystemDeleteHandler fsHandler,
- CacheDeleteHandler cacheHandler) {
+ CacheDeleteHandler cacheHandler,
+ Provider<CurrentUser> userProvider,
+ @PluginName String pluginName) {
this.allProjectsName = allProjectsNameProvider.get();
this.dbHandler = dbHandler;
this.fsHandler = fsHandler;
this.cacheHandler = cacheHandler;
+ this.userProvider = userProvider;
+ this.pluginName = pluginName;
}
@Override
public Object apply(ProjectResource rsrc, Input input)
throws ResourceNotFoundException, ResourceConflictException,
- MethodNotAllowedException, OrmException, IOException {
+ MethodNotAllowedException, OrmException, IOException, AuthException {
+ if (!canDelete(rsrc)) {
+ throw new AuthException("not allowed to delete project");
+ }
+
Project project = rsrc.getControl().getProject();
if (project.getNameKey().equals(allProjectsName)) {
throw new MethodNotAllowedException();
@@ -93,6 +109,14 @@
return Response.none();
}
+ protected boolean canDelete(ProjectResource rsrc) {
+ CapabilityControl ctl = userProvider.get().getCapabilities();
+ return ctl.canAdministrateServer()
+ || ctl.canPerform(pluginName + "-" + DELETE_PROJECT)
+ || (ctl.canPerform(pluginName + "-" + DELETE_OWN_PROJECT)
+ && rsrc.getControl().isOwner());
+ }
+
protected boolean isAllProjects(ProjectResource rsrc) {
return (rsrc.getControl().getProject()
.getNameKey().equals(allProjectsName));
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Module.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Module.java
index 9ee44e0..047b3d9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/Module.java
@@ -15,6 +15,7 @@
package com.googlesource.gerrit.plugins.deleteproject;
import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND;
+import static com.googlesource.gerrit.plugins.deleteproject.DeleteOwnProjectCapability.DELETE_OWN_PROJECT;
import static com.googlesource.gerrit.plugins.deleteproject.DeleteProjectCapability.DELETE_PROJECT;
import com.google.gerrit.extensions.annotations.Exports;
@@ -36,6 +37,9 @@
bind(CapabilityDefinition.class)
.annotatedWith(Exports.named(DELETE_PROJECT))
.to(DeleteProjectCapability.class);
+ bind(CapabilityDefinition.class)
+ .annotatedWith(Exports.named(DELETE_OWN_PROJECT))
+ .to(DeleteOwnProjectCapability.class);
bind(DatabaseDeleteHandler.class)
.to(registerDatabaseHandler());
bind(FilesystemDeleteHandler.class);
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 95182b7..b72ee3a 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -38,7 +38,10 @@
Access
------
-To be allowed to delete projects a user must be a member of a group
-that is granted the 'Delete Project' capability (provided by this
-plugin) or the 'Administrate Server' capability.
+To be allowed to delete arbitrary projects a user must be a member of a
+group that is granted the 'Delete Project' capability (provided by this
+plugin) or the 'Administrate Server' capability. Project owners are
+allowed to delete their own projects if they are member of a group that
+is granted the 'Delete Own Project' capability (provided by this
+plugin).