blob: ac39e7d59b80b76ec05aa33a5e835fdc909063cf [file] [log] [blame]
// Copyright (C) 2015 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.reparent;
import static com.googlesource.gerrit.plugins.reparent.ReparentOwnProjectCapability.REPARENT_OWN_PROJECT;
import static com.googlesource.gerrit.plugins.reparent.ReparentProjectCapability.REPARENT_PROJECT;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.extensions.webui.UiAction;
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.PluginConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SetParent.Input;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@Singleton
public class SetParent implements
RestModifyView<ProjectResource, com.google.gerrit.server.project.SetParent.Input>,
UiAction<ProjectResource>{
private static final String KEY_JAIL = "jail";
private static final String KEY_PROTECTORATE = "protectorate";
private final String pluginName;
private final Provider<CurrentUser> userProvider;
private final AllProjectsName allProjectsName;
private final PluginConfigFactory cfgFactory;
private final ProjectCache projectCache;
private final com.google.gerrit.server.project.SetParent setParent;
@Inject
SetParent(@PluginName String pluginName,
Provider<CurrentUser> userProvider,
AllProjectsName allProjectsName,
PluginConfigFactory cfgFactory,
ProjectCache projectCache,
com.google.gerrit.server.project.SetParent setParent) {
this.pluginName = pluginName;
this.userProvider = userProvider;
this.allProjectsName = allProjectsName;
this.cfgFactory = cfgFactory;
this.projectCache = projectCache;
this.setParent = setParent;
}
@Override
public Object apply(ProjectResource rsrc, Input input) throws AuthException,
ResourceConflictException, ResourceNotFoundException,
UnprocessableEntityException, IOException {
assertReparentPermission(rsrc);
ProjectState newParent = getNewParentProject(input);
assertReparent(rsrc, newParent);
return setParent.apply(rsrc, input, false);
}
private void assertReparent(ProjectResource rsrc, ProjectState newParent)
throws ResourceConflictException {
CapabilityControl ctl = userProvider.get().getCapabilities();
if (ctl.canAdministrateServer()) {
return;
}
PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName);
Set<String> allJails =
new HashSet<>(Arrays.asList(cfg.getStringList(KEY_JAIL)));
Set<String> allProtectorates =
new HashSet<>(Arrays.asList(cfg.getStringList(KEY_PROTECTORATE)));
Set<String> myJails = new HashSet<>();
Set<String> myProtectorates = new HashSet<>();
for (ProjectState p : rsrc.getControl().getProjectState().parents()) {
String name = p.getProject().getName();
if (allJails.contains(name)) {
myJails.add(name);
}
if (allProtectorates.contains(name)) {
myProtectorates.add(name);
}
}
for (ProjectState p : newParent.tree()) {
String name = p.getProject().getName();
if (allProtectorates.contains(name) && !myProtectorates.contains(name)) {
throw new ResourceConflictException(
"not allowed to reparent under project " + name);
}
myJails.remove(name);
}
if (!myJails.isEmpty()) {
throw new ResourceConflictException(
"not allowed to move away from project " + myJails.iterator().next());
}
}
private ProjectState getNewParentProject(Input input)
throws UnprocessableEntityException {
if (input != null) {
String newParent = Strings.emptyToNull(input.parent);
if (newParent != null) {
ProjectState parent = projectCache.get(new Project.NameKey(newParent));
if (parent == null) {
throw new UnprocessableEntityException("parent project " + newParent
+ " not found");
}
return parent;
}
}
return projectCache.getAllProjects();
}
public void assertReparentPermission(ProjectResource rsrc)
throws AuthException {
if (!canReparent(rsrc)) {
throw new AuthException("not allowed to delete project");
}
}
private boolean canReparent(ProjectResource rsrc) {
CapabilityControl ctl = userProvider.get().getCapabilities();
return ctl.canAdministrateServer()
|| ctl.canPerform(pluginName + "-" + REPARENT_PROJECT)
|| (ctl.canPerform(pluginName + "-" + REPARENT_OWN_PROJECT)
&& rsrc.getControl().isOwner());
}
@Override
public UiAction.Description getDescription(ProjectResource rsrc) {
return new UiAction.Description()
.setLabel("Reparent...")
.setTitle(isAllProjects(rsrc)
? String.format("No reparent of %s project",
allProjectsName)
: String.format("Reparent project %s", rsrc.getName()))
.setEnabled(!isAllProjects(rsrc))
.setVisible(canReparent(rsrc));
}
private boolean isAllProjects(ProjectResource rsrc) {
return (rsrc.getControl().getProject()
.getNameKey().equals(allProjectsName));
}
}