diff --git a/scm-core/src/main/java/sonia/scm/repository/RemoveDeletedRepositoryRole.java b/scm-core/src/main/java/sonia/scm/repository/RemoveDeletedRepositoryRole.java new file mode 100644 index 0000000000..25564dca83 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/RemoveDeletedRepositoryRole.java @@ -0,0 +1,48 @@ +package sonia.scm.repository; + +import com.github.legman.Subscribe; +import sonia.scm.EagerSingleton; +import sonia.scm.plugin.Extension; + +import javax.inject.Inject; + +import java.util.Optional; + +import static sonia.scm.HandlerEventType.DELETE; + +@EagerSingleton +@Extension +public class RemoveDeletedRepositoryRole { + + private final RepositoryManager repositoryManager; + + @Inject + public RemoveDeletedRepositoryRole(RepositoryManager repositoryManager) { + this.repositoryManager = repositoryManager; + } + + @Subscribe + void handle(RepositoryRoleEvent event) { + if (event.getEventType() == DELETE) { + repositoryManager.getAll() + .forEach(repository -> check(repository, event.getItem())); + } + } + + private void check(Repository repository, RepositoryRole role) { + findPermission(repository, role) + .ifPresent(permission -> removeFromPermissions(repository, permission)); + } + + private Optional findPermission(Repository repository, RepositoryRole item) { + return repository.getPermissions() + .stream() + .filter(repositoryPermission -> item.getName().equals(repositoryPermission.getRole())) + .findFirst(); + } + + private void removeFromPermissions(Repository repository, RepositoryPermission permission) { + repository.removePermission(permission); + repositoryManager.modify(repository); + } +} diff --git a/scm-core/src/test/java/sonia/scm/repository/RemoveDeletedRepositoryRoleTest.java b/scm-core/src/test/java/sonia/scm/repository/RemoveDeletedRepositoryRoleTest.java new file mode 100644 index 0000000000..f2f88f9ea7 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/repository/RemoveDeletedRepositoryRoleTest.java @@ -0,0 +1,91 @@ +package sonia.scm.repository; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import sonia.scm.HandlerEventType; + +import java.util.Arrays; +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.LENIENT; +import static sonia.scm.HandlerEventType.DELETE; + +@ExtendWith(MockitoExtension.class) +class RemoveDeletedRepositoryRoleTest { + + static final Repository REPOSITORY = createRepositoryWithRoles("with", "deleted", "kept"); + + @Mock + RepositoryManager manager; + @Captor + ArgumentCaptor modifyCaptor; + + private RemoveDeletedRepositoryRole removeDeletedRepositoryRole; + + @BeforeEach + void init() { + removeDeletedRepositoryRole = new RemoveDeletedRepositoryRole(manager); + doNothing().when(manager).modify(modifyCaptor.capture()); + } + + @Test + void shouldRemoveDeletedPermission() { + when(manager.getAll()).thenReturn(Collections.singletonList(REPOSITORY)); + + removeDeletedRepositoryRole.handle(new RepositoryRoleEvent(DELETE, createRole("deleted"))); + + verify(manager).modify(any()); + Assertions.assertThat(modifyCaptor.getValue().getPermissions()) + .containsExactly(createPermission("kept")); + } + + @Test + @MockitoSettings(strictness = LENIENT) + void shouldDoNothingForEventsWithUnusedRole() { + when(manager.getAll()).thenReturn(Collections.singletonList(REPOSITORY)); + + removeDeletedRepositoryRole.handle(new RepositoryRoleEvent(DELETE, createRole("unused"))); + + verify(manager, never()).modify(any()); + } + + @Test + @MockitoSettings(strictness = LENIENT) + void shouldDoNothingForEventsOtherThanDelete() { + when(manager.getAll()).thenReturn(Collections.singletonList(REPOSITORY)); + + Arrays.stream(HandlerEventType.values()) + .filter(type -> type != DELETE) + .forEach( + type -> removeDeletedRepositoryRole.handle(new RepositoryRoleEvent(type, createRole("deleted"))) + ); + + verify(manager, never()).modify(any()); + } + + private RepositoryRole createRole(String name) { + return new RepositoryRole(name, Collections.singleton("x"), "x"); + } + + static Repository createRepositoryWithRoles(String name, String... roles) { + Repository repository = new Repository("x", "git", "space", name); + Arrays.stream(roles).forEach(role -> repository.addPermission(createPermission(role))); + return repository; + } + + private static RepositoryPermission createPermission(String role) { + return new RepositoryPermission("user", role, false); + } +}