* stable-2.16: ssh: Allow GIT_PROTOCOL to contain multiple parameters Fix running Gerrit on Tomcat Fix too-aggressive shortcut Filter MERGE_LIST magic file from Prolog facts Allow to enable git protocol version 2 for upload pack Remove irrelevant styles Remove unneeded AuditServiceImpl Fix some reviewers emails showing as undefined Add shift+m shortcut to diff view Delete system config table Don't use List in GerritConfigListener API Revert "Revert "Show author and committer when relevant"" Reload repo and group list after creating a repo or group Hide "private" check box if private changes are disabled Set version to 2.15.7 Trigger audit for GIT over Http commands Change-Id: Idf8aed46f7a4fc1cd9b2630f79c73b86eefd970f
194 lines
7.9 KiB
Java
194 lines
7.9 KiB
Java
// Copyright (C) 2012 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.restapi.project;
|
|
|
|
import static java.util.Objects.requireNonNull;
|
|
|
|
import com.google.common.base.MoreObjects;
|
|
import com.google.common.base.Strings;
|
|
import com.google.common.collect.Iterables;
|
|
import com.google.common.collect.Multimap;
|
|
import com.google.gerrit.extensions.api.projects.ParentInput;
|
|
import com.google.gerrit.extensions.restapi.AuthException;
|
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
|
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.reviewdb.client.Project;
|
|
import com.google.gerrit.server.IdentifiedUser;
|
|
import com.google.gerrit.server.config.AllProjectsName;
|
|
import com.google.gerrit.server.config.AllUsersName;
|
|
import com.google.gerrit.server.config.ConfigKey;
|
|
import com.google.gerrit.server.config.ConfigUpdatedEvent;
|
|
import com.google.gerrit.server.config.ConfigUpdatedEvent.ConfigUpdateEntry;
|
|
import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
|
|
import com.google.gerrit.server.config.GerritConfigListener;
|
|
import com.google.gerrit.server.config.GerritServerConfig;
|
|
import com.google.gerrit.server.git.meta.MetaDataUpdate;
|
|
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.permissions.ProjectPermission;
|
|
import com.google.gerrit.server.project.ProjectCache;
|
|
import com.google.gerrit.server.project.ProjectConfig;
|
|
import com.google.gerrit.server.project.ProjectResource;
|
|
import com.google.gerrit.server.project.ProjectState;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.Singleton;
|
|
import java.io.IOException;
|
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
|
import org.eclipse.jgit.lib.Config;
|
|
|
|
@Singleton
|
|
public class SetParent
|
|
implements RestModifyView<ProjectResource, ParentInput>, GerritConfigListener {
|
|
private final ProjectCache cache;
|
|
private final PermissionBackend permissionBackend;
|
|
private final MetaDataUpdate.Server updateFactory;
|
|
private final AllProjectsName allProjects;
|
|
private final AllUsersName allUsers;
|
|
private final ProjectConfig.Factory projectConfigFactory;
|
|
private volatile boolean allowProjectOwnersToChangeParent;
|
|
|
|
@Inject
|
|
SetParent(
|
|
ProjectCache cache,
|
|
PermissionBackend permissionBackend,
|
|
MetaDataUpdate.Server updateFactory,
|
|
AllProjectsName allProjects,
|
|
AllUsersName allUsers,
|
|
ProjectConfig.Factory projectConfigFactory,
|
|
@GerritServerConfig Config config) {
|
|
this.cache = cache;
|
|
this.permissionBackend = permissionBackend;
|
|
this.updateFactory = updateFactory;
|
|
this.allProjects = allProjects;
|
|
this.allUsers = allUsers;
|
|
this.projectConfigFactory = projectConfigFactory;
|
|
this.allowProjectOwnersToChangeParent =
|
|
config.getBoolean("receive", "allowProjectOwnersToChangeParent", false);
|
|
}
|
|
|
|
@Override
|
|
public String apply(ProjectResource rsrc, ParentInput input)
|
|
throws AuthException, ResourceConflictException, ResourceNotFoundException,
|
|
UnprocessableEntityException, IOException, PermissionBackendException,
|
|
BadRequestException {
|
|
return apply(rsrc, input, true);
|
|
}
|
|
|
|
public String apply(ProjectResource rsrc, ParentInput input, boolean checkIfAdmin)
|
|
throws AuthException, ResourceConflictException, ResourceNotFoundException,
|
|
UnprocessableEntityException, IOException, PermissionBackendException,
|
|
BadRequestException {
|
|
IdentifiedUser user = rsrc.getUser().asIdentifiedUser();
|
|
String parentName =
|
|
MoreObjects.firstNonNull(Strings.emptyToNull(input.parent), allProjects.get());
|
|
validateParentUpdate(rsrc.getProjectState().getNameKey(), user, parentName, checkIfAdmin);
|
|
try (MetaDataUpdate md = updateFactory.create(rsrc.getNameKey())) {
|
|
ProjectConfig config = projectConfigFactory.read(md);
|
|
Project project = config.getProject();
|
|
project.setParentName(parentName);
|
|
|
|
String msg = Strings.emptyToNull(input.commitMessage);
|
|
if (msg == null) {
|
|
msg = String.format("Changed parent to %s.\n", parentName);
|
|
} else if (!msg.endsWith("\n")) {
|
|
msg += "\n";
|
|
}
|
|
md.setAuthor(user);
|
|
md.setMessage(msg);
|
|
config.commit(md);
|
|
cache.evict(rsrc.getProjectState().getProject());
|
|
|
|
Project.NameKey parent = project.getParent(allProjects);
|
|
requireNonNull(parent);
|
|
return parent.get();
|
|
} catch (RepositoryNotFoundException notFound) {
|
|
throw new ResourceNotFoundException(rsrc.getName());
|
|
} catch (ConfigInvalidException e) {
|
|
throw new ResourceConflictException(
|
|
String.format("invalid project.config: %s", e.getMessage()));
|
|
}
|
|
}
|
|
|
|
public void validateParentUpdate(
|
|
Project.NameKey project, IdentifiedUser user, String newParent, boolean checkIfAdmin)
|
|
throws AuthException, ResourceConflictException, UnprocessableEntityException,
|
|
PermissionBackendException, BadRequestException {
|
|
if (checkIfAdmin) {
|
|
if (allowProjectOwnersToChangeParent) {
|
|
permissionBackend.user(user).project(project).check(ProjectPermission.WRITE_CONFIG);
|
|
} else {
|
|
permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
|
|
}
|
|
}
|
|
|
|
if (project.equals(allUsers) && !allProjects.get().equals(newParent)) {
|
|
throw new BadRequestException(
|
|
String.format("%s must inherit from %s", allUsers.get(), allProjects.get()));
|
|
}
|
|
|
|
if (project.equals(allProjects)) {
|
|
throw new ResourceConflictException("cannot set parent of " + allProjects.get());
|
|
}
|
|
|
|
if (allUsers.get().equals(newParent)) {
|
|
throw new ResourceConflictException(
|
|
String.format("Cannot inherit from '%s' project", allUsers.get()));
|
|
}
|
|
|
|
newParent = Strings.emptyToNull(newParent);
|
|
if (newParent != null) {
|
|
ProjectState parent = cache.get(new Project.NameKey(newParent));
|
|
if (parent == null) {
|
|
throw new UnprocessableEntityException("parent project " + newParent + " not found");
|
|
}
|
|
|
|
if (parent.getName().equals(project.get())) {
|
|
throw new ResourceConflictException("cannot set parent to self");
|
|
}
|
|
|
|
if (Iterables.tryFind(
|
|
parent.tree(),
|
|
p -> {
|
|
return p.getNameKey().equals(project);
|
|
})
|
|
.isPresent()) {
|
|
throw new ResourceConflictException(
|
|
"cycle exists between " + project.get() + " and " + parent.getName());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Multimap<UpdateResult, ConfigUpdateEntry> configUpdated(ConfigUpdatedEvent event) {
|
|
ConfigKey receiveSetParent = ConfigKey.create("receive", "allowProjectOwnersToChangeParent");
|
|
if (!event.isValueUpdated(receiveSetParent)) {
|
|
return ConfigUpdatedEvent.NO_UPDATES;
|
|
}
|
|
try {
|
|
boolean enabled =
|
|
event.getNewConfig().getBoolean("receive", "allowProjectOwnersToChangeParent", false);
|
|
this.allowProjectOwnersToChangeParent = enabled;
|
|
} catch (IllegalArgumentException iae) {
|
|
return event.reject(receiveSetParent);
|
|
}
|
|
return event.accept(receiveSetParent);
|
|
}
|
|
}
|