| // Copyright (C) 2019 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.plugins.checks.acceptance; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.acceptance.GitUtil.deleteRef; |
| import static com.google.gerrit.acceptance.GitUtil.fetch; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel; |
| import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.acceptance.SkipProjectClone; |
| import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; |
| import com.google.gerrit.common.data.Permission; |
| import com.google.gerrit.entities.BranchNameKey; |
| import com.google.gerrit.entities.Change; |
| import com.google.gerrit.entities.Project; |
| import com.google.gerrit.extensions.client.ChangeStatus; |
| import com.google.gerrit.extensions.common.ChangeInput; |
| import com.google.gerrit.extensions.restapi.ResourceConflictException; |
| import com.google.gerrit.plugins.checks.CheckerRef; |
| import com.google.gerrit.plugins.checks.CheckerUuid; |
| import com.google.gerrit.server.change.ChangeInserter; |
| import com.google.gerrit.server.notedb.Sequences; |
| import com.google.gerrit.server.update.BatchUpdate; |
| import com.google.gerrit.server.util.time.TimeUtil; |
| import com.google.inject.Inject; |
| import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.lib.ObjectInserter; |
| import org.eclipse.jgit.lib.ObjectReader; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| import org.eclipse.jgit.transport.PushResult; |
| import org.eclipse.jgit.transport.RemoteRefUpdate; |
| import org.junit.Test; |
| |
| @SkipProjectClone |
| public class CheckerRefsIT extends AbstractCheckersTest { |
| @Inject private ProjectOperations projectOperations; |
| @Inject private Sequences seq; |
| @Inject private ChangeInserter.Factory changeInserterFactory; |
| @Inject private BatchUpdate.Factory updateFactory; |
| |
| @Test |
| public void cannotCreateCheckerRef() throws Exception { |
| projectOperations |
| .project(allProjects) |
| .forUpdate() |
| .add(allow(Permission.CREATE).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| |
| String checkerRef = CheckerUuid.parse("test:my-checker").toRefName(); |
| |
| TestRepository<InMemoryRepository> testRepo = cloneProject(allProjects); |
| PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to(checkerRef); |
| r.assertErrorStatus(); |
| assertThat(r.getMessage()).contains("Not allowed to create checker ref."); |
| |
| try (Repository repo = repoManager.openRepository(allProjects)) { |
| assertThat(repo.exactRef(checkerRef)).isNull(); |
| } |
| } |
| |
| @Test |
| public void canCreateCheckerLikeRef() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.CREATE).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| |
| String checkerRef = CheckerUuid.parse("test:my-checker").toRefName(); |
| |
| // checker ref can be created in any project except All-Projects |
| TestRepository<InMemoryRepository> testRepo = cloneProject(project); |
| PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to(checkerRef); |
| r.assertOkStatus(); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| assertThat(repo.exactRef(checkerRef)).isNotNull(); |
| } |
| } |
| |
| @Test |
| public void cannotDeleteCheckerRef() throws Exception { |
| projectOperations |
| .project(allProjects) |
| .forUpdate() |
| .add( |
| allow(Permission.DELETE) |
| .ref(CheckerRef.REFS_CHECKERS + "*") |
| .group(REGISTERED_USERS) |
| .force(true)) |
| .update(); |
| |
| CheckerUuid checkerUuid = checkerOperations.newChecker().create(); |
| String checkerRef = checkerUuid.toRefName(); |
| |
| TestRepository<InMemoryRepository> testRepo = cloneProject(allProjects); |
| PushResult r = deleteRef(testRepo, checkerRef); |
| RemoteRefUpdate refUpdate = r.getRemoteUpdate(checkerRef); |
| assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON); |
| assertThat(refUpdate.getMessage()).contains("Not allowed to delete checker ref."); |
| |
| try (Repository repo = repoManager.openRepository(allProjects)) { |
| assertThat(repo.exactRef(checkerRef)).isNotNull(); |
| } |
| } |
| |
| @Test |
| public void canDeleteCheckerLikeRef() throws Exception { |
| String checkerRef = CheckerUuid.parse("foo:bar").toRefName(); |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add( |
| allow(Permission.DELETE) |
| .ref(CheckerRef.REFS_CHECKERS + "*") |
| .group(REGISTERED_USERS) |
| .force(true)) |
| .add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid())) |
| .update(); |
| createBranch(BranchNameKey.create(project, checkerRef)); |
| |
| // checker ref can be deleted in any project except All-Projects |
| TestRepository<InMemoryRepository> testRepo = cloneProject(project); |
| PushResult r = deleteRef(testRepo, checkerRef); |
| RemoteRefUpdate refUpdate = r.getRemoteUpdate(checkerRef); |
| assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| assertThat(repo.exactRef(checkerRef)).isNull(); |
| } |
| } |
| |
| @Test |
| public void updateCheckerRefsByPushIsDisabled() throws Exception { |
| CheckerUuid checkerUuid = checkerOperations.newChecker().create(); |
| String checkerRef = checkerUuid.toRefName(); |
| |
| TestRepository<InMemoryRepository> repo = cloneProject(allProjects, admin); |
| fetch(repo, checkerRef + ":checkerRef"); |
| repo.reset("checkerRef"); |
| |
| projectOperations |
| .project(allProjects) |
| .forUpdate() |
| .add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| PushOneCommit.Result r = pushFactory.create(admin.newIdent(), repo).to(checkerRef); |
| r.assertErrorStatus(); |
| r.assertMessage("direct update of checker ref not allowed"); |
| } |
| |
| @Test |
| public void updateCheckerLikeRefByPush() throws Exception { |
| String checkerRef = CheckerUuid.parse("foo:bar").toRefName(); |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid())) |
| .update(); |
| createBranch(BranchNameKey.create(project, checkerRef)); |
| |
| TestRepository<InMemoryRepository> repo = cloneProject(project, admin); |
| fetch(repo, checkerRef + ":checkerRef"); |
| repo.reset("checkerRef"); |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| PushOneCommit.Result r = pushFactory.create(admin.newIdent(), repo).to(checkerRef); |
| r.assertOkStatus(); |
| } |
| |
| @Test |
| public void submitToCheckerRefsIsDisabled() throws Exception { |
| CheckerUuid checkerUuid = checkerOperations.newChecker().disable().create(); |
| String checkerRef = checkerUuid.toRefName(); |
| String changeId = createChangeWithoutCommitValidation(allProjects, checkerRef); |
| |
| projectOperations |
| .project(allProjects) |
| .forUpdate() |
| .add( |
| allowLabel("Code-Review") |
| .ref(CheckerRef.REFS_CHECKERS + "*") |
| .group(adminGroupUuid()) |
| .range(-2, 2)) |
| .update(); |
| approve(changeId); |
| |
| projectOperations |
| .project(allProjects) |
| .forUpdate() |
| .add(allow(Permission.SUBMIT).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| |
| ResourceConflictException thrown = |
| assertThrows( |
| ResourceConflictException.class, () -> gApi.changes().id(changeId).current().submit()); |
| assertThat(thrown).hasMessageThat().contains("submit to checker ref not allowed"); |
| } |
| |
| @Test |
| public void submitToCheckerLikeRef() throws Exception { |
| String checkerRef = CheckerUuid.parse("foo:bar").toRefName(); |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid())) |
| .update(); |
| createBranch(BranchNameKey.create(project, checkerRef)); |
| |
| String changeId = createChangeWithoutCommitValidation(project, checkerRef); |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add( |
| allowLabel("Code-Review") |
| .ref(CheckerRef.REFS_CHECKERS + "*") |
| .group(adminGroupUuid()) |
| .range(-2, 2)) |
| .update(); |
| approve(changeId); |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.SUBMIT).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| |
| // submitting to a checker ref should work in any project except All-Projects |
| gApi.changes().id(changeId).current().submit(); |
| |
| assertThat(gApi.changes().id(changeId).get().status).isEqualTo(ChangeStatus.MERGED); |
| } |
| |
| @Test |
| public void createChangeForCheckerRefsByPushIsDisabled() throws Exception { |
| CheckerUuid checkerUuid = checkerOperations.newChecker().create(); |
| String checkerRef = checkerUuid.toRefName(); |
| |
| TestRepository<InMemoryRepository> repo = cloneProject(allProjects, admin); |
| fetch(repo, checkerRef + ":checkerRef"); |
| repo.reset("checkerRef"); |
| |
| projectOperations |
| .project(allProjects) |
| .forUpdate() |
| .add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| PushOneCommit.Result r = |
| pushFactory.create(admin.newIdent(), repo).to("refs/for/" + checkerRef); |
| r.assertErrorStatus(); |
| r.assertMessage("creating change for checker ref not allowed"); |
| } |
| |
| @Test |
| public void createChangeForCheckerLikeRefByPush() throws Exception { |
| String checkerRef = CheckerUuid.parse("foo:bar").toRefName(); |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid())) |
| .update(); |
| createBranch(BranchNameKey.create(project, checkerRef)); |
| |
| TestRepository<InMemoryRepository> repo = cloneProject(project, admin); |
| fetch(repo, checkerRef + ":checkerRef"); |
| repo.reset("checkerRef"); |
| |
| // creating a change on a checker ref by push should work in any project except All-Projects |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.PUSH).ref(CheckerRef.REFS_CHECKERS + "*").group(adminGroupUuid())) |
| .update(); |
| PushOneCommit.Result r = |
| pushFactory.create(admin.newIdent(), repo).to("refs/for/" + checkerRef); |
| r.assertOkStatus(); |
| } |
| |
| @Test |
| public void createChangeForCheckerRefsViaApiIsDisabled() throws Exception { |
| CheckerUuid checkerUuid = checkerOperations.newChecker().create(); |
| String checkerRef = checkerUuid.toRefName(); |
| |
| TestRepository<InMemoryRepository> repo = cloneProject(allProjects, admin); |
| fetch(repo, checkerRef + ":checkerRef"); |
| repo.reset("checkerRef"); |
| RevCommit head = getHead(repo.getRepository(), "HEAD"); |
| |
| ChangeInput input = new ChangeInput(); |
| input.project = allProjects.get(); |
| input.branch = checkerRef; |
| input.baseCommit = head.name(); |
| input.subject = "A change."; |
| |
| ResourceConflictException thrown = |
| assertThrows(ResourceConflictException.class, () -> gApi.changes().create(input)); |
| assertThat(thrown).hasMessageThat().contains("creating change for checker ref not allowed"); |
| } |
| |
| @Test |
| public void createChangeForCheckerLikeRefViaApi() throws Exception { |
| String checkerRef = CheckerUuid.parse("foo:bar").toRefName(); |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid())) |
| .update(); |
| createBranch(BranchNameKey.create(project, checkerRef)); |
| |
| TestRepository<InMemoryRepository> repo = cloneProject(project, admin); |
| fetch(repo, checkerRef + ":checkerRef"); |
| repo.reset("checkerRef"); |
| RevCommit head = getHead(repo.getRepository(), "HEAD"); |
| |
| // creating a change on a checker ref via API should work in any project except All-Projects |
| ChangeInput input = new ChangeInput(); |
| input.project = project.get(); |
| input.branch = checkerRef; |
| input.baseCommit = head.name(); |
| input.subject = "A change."; |
| assertThat(gApi.changes().create(input).get()).isNotNull(); |
| } |
| |
| private String createChangeWithoutCommitValidation(Project.NameKey project, String targetRef) |
| throws Exception { |
| try (Repository git = repoManager.openRepository(project); |
| ObjectInserter oi = git.newObjectInserter(); |
| ObjectReader reader = oi.newReader(); |
| RevWalk rw = new RevWalk(reader); |
| TestRepository<Repository> testRepo = new TestRepository<>(git)) { |
| RevCommit head = rw.parseCommit(git.exactRef(targetRef).getObjectId()); |
| RevCommit commit = |
| testRepo |
| .commit() |
| .author(admin.newIdent()) |
| .message("A change.") |
| .insertChangeId() |
| .parent(head) |
| .create(); |
| |
| Change.Id changeId = Change.id(seq.nextChangeId()); |
| ChangeInserter ins = changeInserterFactory.create(changeId, commit, targetRef); |
| ins.setValidate(false); |
| ins.setMessage(String.format("Uploaded patch set %s.", ins.getPatchSetId().get())); |
| try (BatchUpdate bu = |
| updateFactory.create( |
| project, identifiedUserFactory.create(admin.id()), TimeUtil.nowTs())) { |
| bu.setRepository(git, rw, oi); |
| bu.insertChange(ins); |
| bu.execute(); |
| } |
| return changeId.toString(); |
| } |
| } |
| } |