blob: 1607f09d768889458dc6de0a634965341fb9c59f [file] [log] [blame]
// Copyright (C) 2017 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.acceptance.api.group;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInput;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.gerrit.server.group.db.testing.GroupTestUtil;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.RefRename;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
/**
* Checks that invalid group configurations are flagged. Since the inconsistencies are global to the
* test server configuration, and leak from one test method into the next one, there is no way for
* this test to not be sandboxed.
*/
@Sandboxed
@NoHttpd
public class GroupsConsistencyIT extends AbstractDaemonTest {
@Inject protected GroupOperations groupOperations;
@Inject private ProjectOperations projectOperations;
private GroupInfo gAdmin;
private GroupInfo g1;
private GroupInfo g2;
private static final String BOGUS_UUID = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
@Before
public void basicSetup() throws Exception {
projectOperations
.allProjectsForUpdate()
.add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
.update();
String name1 = groupOperations.newGroup().name("g1").create().get();
String name2 = groupOperations.newGroup().name("g2").create().get();
gApi.groups().id(name1).addMembers(user.fullName());
gApi.groups().id(name2).addMembers(admin.fullName());
gApi.groups().id(name1).addGroups(name2);
this.g1 = gApi.groups().id(name1).detail();
this.g2 = gApi.groups().id(name2).detail();
this.gAdmin = gApi.groups().id("Administrators").detail();
}
@Test
public void allGood() throws Exception {
assertThat(check()).isEmpty();
}
@Test
public void missingGroupNameRef() throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
RefUpdate ru = repo.updateRef(RefNames.REFS_GROUPNAMES);
ru.setForceUpdate(true);
RefUpdate.Result result = testRefAction(() -> ru.delete());
assertThat(result).isEqualTo(Result.FORCED);
}
assertError("refs/meta/group-names does not exist");
}
@Test
public void missingGroupRef() throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
RefUpdate ru = repo.updateRef(RefNames.refsGroups(AccountGroup.uuid(g1.id)));
ru.setForceUpdate(true);
RefUpdate.Result result = testRefAction(() -> ru.delete());
assertThat(result).isEqualTo(Result.FORCED);
}
assertError("missing as group ref");
}
@Test
public void parseGroupRef() throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
RefRename ru =
repo.renameRef(
RefNames.refsGroups(AccountGroup.uuid(g1.id)), RefNames.REFS_GROUPS + BOGUS_UUID);
RefUpdate.Result result = testRefAction(() -> ru.rename());
assertThat(result).isEqualTo(Result.RENAMED);
}
assertError("null UUID from");
}
@Test
public void missingNameEntry() throws Exception {
try (Repository repo = repoManager.openRepository(allUsers)) {
RefRename ru =
repo.renameRef(
RefNames.refsGroups(AccountGroup.uuid(g1.id)),
RefNames.refsGroups(AccountGroup.uuid(BOGUS_UUID)));
RefUpdate.Result result = testRefAction(() -> ru.rename());
assertThat(result).isEqualTo(Result.RENAMED);
}
assertError("group " + BOGUS_UUID + " has no entry in name map");
}
@Test
public void groupRefDoesNotParse() throws Exception {
updateGroupFile(
RefNames.refsGroups(AccountGroup.uuid(g1.id)),
GroupConfig.GROUP_CONFIG_FILE,
"[this is not valid\n");
assertError("does not parse");
}
@Test
public void nameRefDoesNotParse() throws Exception {
updateGroupFile(
RefNames.REFS_GROUPNAMES,
GroupNameNotes.getNoteKey(AccountGroup.nameKey(g1.name)).getName(),
"[this is not valid\n");
assertError("does not parse");
}
@Test
public void inconsistentName() throws Exception {
Config cfg = new Config();
cfg.setString("group", null, "name", "not really");
cfg.setString("group", null, "id", "42");
cfg.setString("group", null, "ownerGroupUuid", gAdmin.id);
updateGroupFile(
RefNames.refsGroups(AccountGroup.uuid(g1.id)), GroupConfig.GROUP_CONFIG_FILE, cfg.toText());
assertError("inconsistent name");
}
@Test
public void sharedGroupID() throws Exception {
Config cfg = new Config();
cfg.setString("group", null, "name", g1.name);
cfg.setInt("group", null, "id", g2.groupId);
cfg.setString("group", null, "ownerGroupUuid", gAdmin.id);
updateGroupFile(
RefNames.refsGroups(AccountGroup.uuid(g1.id)), GroupConfig.GROUP_CONFIG_FILE, cfg.toText());
assertError("shared group id");
}
@Test
public void unknownOwnerGroup() throws Exception {
Config cfg = new Config();
cfg.setString("group", null, "name", g1.name);
cfg.setInt("group", null, "id", g1.groupId);
cfg.setString("group", null, "ownerGroupUuid", BOGUS_UUID);
updateGroupFile(
RefNames.refsGroups(AccountGroup.uuid(g1.id)), GroupConfig.GROUP_CONFIG_FILE, cfg.toText());
assertError("nonexistent owner group");
}
@Test
public void nameWithoutGroupRef() throws Exception {
String bogusName = "bogus name";
Config config = new Config();
config.setString("group", null, "uuid", BOGUS_UUID);
config.setString("group", null, "name", bogusName);
updateGroupFile(
RefNames.REFS_GROUPNAMES,
GroupNameNotes.getNoteKey(AccountGroup.nameKey(bogusName)).getName(),
config.toText());
assertError("entry missing as group ref");
}
@Test
public void nonexistentMember() throws Exception {
updateGroupFile(RefNames.refsGroups(AccountGroup.uuid(g1.id)), "members", "314159265\n");
assertError("nonexistent member 314159265");
}
@Test
public void nonexistentSubgroup() throws Exception {
updateGroupFile(RefNames.refsGroups(AccountGroup.uuid(g1.id)), "subgroups", BOGUS_UUID + "\n");
assertError("has nonexistent subgroup");
}
@Test
public void cyclicSubgroup() throws Exception {
updateGroupFile(RefNames.refsGroups(AccountGroup.uuid(g1.id)), "subgroups", g1.id + "\n");
assertWarning("cycle");
}
private void assertError(String msg) throws Exception {
assertConsistency(msg, ConsistencyProblemInfo.Status.ERROR);
}
private void assertWarning(String msg) throws Exception {
assertConsistency(msg, ConsistencyProblemInfo.Status.WARNING);
}
private List<ConsistencyProblemInfo> check() throws Exception {
ConsistencyCheckInput in = new ConsistencyCheckInput();
in.checkGroups = new ConsistencyCheckInput.CheckGroupsInput();
ConsistencyCheckInfo info = gApi.config().server().checkConsistency(in);
return info.checkGroupsResult.problems;
}
private void assertConsistency(String msg, ConsistencyProblemInfo.Status want) throws Exception {
List<ConsistencyProblemInfo> problems = check();
for (ConsistencyProblemInfo i : problems) {
if (!i.status.equals(want)) {
continue;
}
if (i.message.contains(msg)) {
return;
}
}
assertWithMessage(String.format("could not find %s substring '%s' in %s", want, msg, problems))
.fail();
}
private void updateGroupFile(String refName, String fileName, String content) throws Exception {
GroupTestUtil.updateGroupFile(
repoManager, allUsers, serverIdent.get(), refName, fileName, content);
}
}