Merge stable-3.6 into master
Change-Id: I2b322fc6d3ab08b4c937bed9ebbc0eb7d969c49f
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java
index 6d4783e..f4ef36a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommits.java
@@ -51,27 +51,31 @@
PersonIdent committer = receiveEvent.commit.getCommitterIdent();
ServiceUserInfo serviceUser = serviceUserResolver.getAsServiceUser(committer);
if (serviceUser != null) {
- if (serviceUser.owner != null
- && serviceUserResolver.listActiveOwners(serviceUser).isEmpty()) {
- throw new CommitValidationException(
- String.format(
- "Commit %s of service user %s (%s) is rejected because "
- + "all service user owner accounts are inactive.",
- receiveEvent.commit.getId().getName(),
- committer.getName(),
- committer.getEmailAddress()));
- }
Optional<AccountState> creator =
accountCache.get(Account.id(serviceUser.createdBy._accountId));
- if (!creator.isPresent() || !creator.get().account().isActive()) {
- throw new CommitValidationException(
- String.format(
- "Commit %s of service user %s (%s) is rejected because "
- + "the account of the service creator is inactive.",
- receiveEvent.commit.getId().getName(),
- committer.getName(),
- committer.getEmailAddress()));
+
+ if (creator.isPresent() && creator.get().account().isActive()) {
+ return Collections.emptyList();
}
+
+ if (!serviceUserResolver.listActiveOwners(serviceUser).isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ StringBuilder msg = new StringBuilder();
+ msg.append(
+ String.format(
+ "Commit %s of service user %s (%s) is rejected because "
+ + " the account which created the service user is inactive",
+ receiveEvent.commit.getId().name(),
+ committer.getName(),
+ committer.getEmailAddress()));
+
+ if (serviceUser.owner != null) {
+ msg.append(" and all accounts in the owner group are inactive");
+ }
+
+ throw new CommitValidationException(msg.toString());
}
} catch (RestApiException e) {
log.error(e.getMessage(), e);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommitsTest.java b/src/test/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommitsTest.java
new file mode 100644
index 0000000..ab6a8b4
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/serviceuser/ValidateServiceUserCommitsTest.java
@@ -0,0 +1,165 @@
+// Copyright (C) 2023 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.serviceuser;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.events.CommitReceivedEvent;
+import com.google.gerrit.server.git.validators.CommitValidationException;
+import com.googlesource.gerrit.plugins.serviceuser.GetServiceUser.ServiceUserInfo;
+import java.util.Collections;
+import java.util.Optional;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ValidateServiceUserCommitsTest {
+ private static final Account.Id SERVICE_USER_ACCOUNT_ID = Account.id(100);
+ private static final Account.Id CREATOR_ACCOUNT_ID = Account.id(200);
+ private static final Account.Id OWNER_ACCOUNT_ID = Account.id(300);
+
+ @Mock ServiceUserResolver resolver;
+ @Mock AccountCache accountCache;
+
+ TestRepository<?> testRepo;
+ CommitReceivedEvent event = new CommitReceivedEvent();
+ AccountInfo serviceUserAccountInfo = new AccountInfo(SERVICE_USER_ACCOUNT_ID.get());
+ PersonIdent serviceUserIdent = new PersonIdent("robot", "robot@example.com");
+
+ ValidateServiceUserCommits validator;
+
+ @Before
+ public void setUp() throws Exception {
+ testRepo = new TestRepository<>(new InMemoryRepository(new DfsRepositoryDescription("repo")));
+ event.commit = testRepo.commit().committer(serviceUserIdent).create();
+ validator = new ValidateServiceUserCommits(resolver, accountCache);
+ }
+
+ @Test
+ public void committerNotServiceUser_pass() throws Exception {
+ when(resolver.getAsServiceUser(serviceUserIdent)).thenReturn(null);
+ assertThat(validator.onCommitReceived(event)).isEmpty();
+ }
+
+ @Test
+ public void noCreatorAndNoActiveOwners_reject() throws Exception {
+ ServiceUserInfo serviceUser = setupWithNonExistingCreator();
+ ownerGroupNotSet(serviceUser);
+ assertThrows(CommitValidationException.class, () -> validator.onCommitReceived(event));
+
+ ownerGroupSetButContainsNoActiveMembers(serviceUser);
+ assertThrows(CommitValidationException.class, () -> validator.onCommitReceived(event));
+ }
+
+ @Test
+ public void inactiveCreatorAndNoActiveOwners_reject() throws Exception {
+ ServiceUserInfo serviceUser = setupWithInactiveCreator();
+ ownerGroupNotSet(serviceUser);
+ assertThrows(CommitValidationException.class, () -> validator.onCommitReceived(event));
+
+ ownerGroupSetButContainsNoActiveMembers(serviceUser);
+ assertThrows(CommitValidationException.class, () -> validator.onCommitReceived(event));
+ }
+
+ @Test
+ public void activeCreatorAnNoActiveOwners_pass() throws Exception {
+ ServiceUserInfo serviceUser = setupWithActiveCreator();
+ ownerGroupNotSet(serviceUser);
+ assertThat(validator.onCommitReceived(event)).isEmpty();
+
+ ownerGroupSetButContainsNoActiveMembers(serviceUser);
+ assertThat(validator.onCommitReceived(event)).isEmpty();
+ }
+
+ @Test
+ public void inactiveCreatorAndActiveOwners_pass() throws Exception {
+ ServiceUserInfo serviceUser = setupWithInactiveCreator();
+ ownerGroupSetAndContainsActiveMembers(serviceUser);
+ assertThat(validator.onCommitReceived(event)).isEmpty();
+ }
+
+ @Test
+ public void activeCreatorAndActiveOwners_pass() throws Exception {
+ ServiceUserInfo serviceUser = setupWithActiveCreator();
+ ownerGroupSetAndContainsActiveMembers(serviceUser);
+ assertThat(validator.onCommitReceived(event)).isEmpty();
+ }
+
+ private ServiceUserInfo setupWithNonExistingCreator() throws Exception {
+ ServiceUserInfo serviceUser = new ServiceUserInfo(serviceUserAccountInfo);
+ when(resolver.getAsServiceUser(serviceUserIdent)).thenReturn(serviceUser);
+
+ serviceUser.createdBy = new AccountInfo(CREATOR_ACCOUNT_ID.get());
+ // accountCache returns empty Optional: means creator account not found
+ when(accountCache.get(CREATOR_ACCOUNT_ID)).thenReturn(Optional.empty());
+
+ return serviceUser;
+ }
+
+ private ServiceUserInfo setupWithActiveCreator() throws Exception {
+ return setupWithCreator(true);
+ }
+
+ private ServiceUserInfo setupWithInactiveCreator() throws Exception {
+ return setupWithCreator(false);
+ }
+
+ private ServiceUserInfo setupWithCreator(boolean active) throws Exception {
+ ServiceUserInfo serviceUser = new ServiceUserInfo(serviceUserAccountInfo);
+ when(resolver.getAsServiceUser(serviceUserIdent)).thenReturn(serviceUser);
+
+ serviceUser.createdBy = new AccountInfo(CREATOR_ACCOUNT_ID.get());
+ AccountState creatorAccountState = mock(AccountState.class);
+ Account creatorAccount = mock(Account.class);
+ when(creatorAccountState.account()).thenReturn(creatorAccount);
+ when(creatorAccount.isActive()).thenReturn(active);
+ when(accountCache.get(CREATOR_ACCOUNT_ID)).thenReturn(Optional.of(creatorAccountState));
+
+ return serviceUser;
+ }
+
+ private void ownerGroupNotSet(ServiceUserInfo serviceUser) {
+ serviceUser.owner = null;
+ }
+
+ private void ownerGroupSetButContainsNoActiveMembers(ServiceUserInfo serviceUser)
+ throws Exception {
+ serviceUser.owner = new GroupInfo();
+ lenient().when(resolver.listActiveOwners(serviceUser)).thenReturn(Collections.emptyList());
+ }
+
+ private void ownerGroupSetAndContainsActiveMembers(ServiceUserInfo serviceUser) throws Exception {
+ serviceUser.owner = new GroupInfo();
+ lenient()
+ .when(resolver.listActiveOwners(serviceUser))
+ .thenReturn(Collections.singletonList(new AccountInfo(OWNER_ACCOUNT_ID.get())));
+ }
+}