From e369e62d2109597e2254fcd122edc55136b3ad6c Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Sat, 11 Jan 2025 15:51:27 -0300 Subject: [PATCH 1/8] added some tests --- .../rbac/AccessControlServiceTest.java | 169 ++++++++++++++++++ api/src/test/resources/application-test.yml | 2 +- 2 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java new file mode 100644 index 000000000..39cce976b --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java @@ -0,0 +1,169 @@ +package io.kafbat.ui.service.rbac; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.config.auth.RbacUser; +import io.kafbat.ui.config.auth.RoleBasedAccessControlProperties; +import io.kafbat.ui.model.rbac.AccessContext; +import java.util.List; +import io.kafbat.ui.model.rbac.Role; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.util.ReflectionTestUtils; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +//@ContextConfiguration(initializers = {AccessControlServiceTest.PropertiesInitializer.class}) +class AccessControlServiceTest extends AbstractIntegrationTest { + +// public static class PropertiesInitializer extends AbstractIntegrationTest.Initializer +// implements ApplicationContextInitializer { + +// @Override +// public void initialize(ConfigurableApplicationContext applicationContext) { +// System.setProperty("rbac.roles[0].name", "memelords"); +// System.setProperty("rbac.roles[0].clusters[0]", "local"); +// +// System.setProperty("rbac.roles[0].subjects[0].provider", "oauth_google"); +// System.setProperty("rbac.roles[0].subjects[0].type", "domain"); +// System.setProperty("rbac.roles[0].subjects[0].value", "katbat.dev"); +// +// System.setProperty("rbac.roles[0].subjects[1].provider", "oauth_google"); +// System.setProperty("rbac.roles[0].subjects[1].type", "user"); +// System.setProperty("rbac.roles[0].subjects[1].value", "name@kafbat.dev"); +// +// System.setProperty("rbac.roles[0].permissions[0].resource", "applicationconfig"); +// System.setProperty("rbac.roles[0].permissions[0].actions", "all"); +// +// super.initialize(applicationContext); +// } +// } + + @Autowired + AccessControlService accessControlService; + + @Mock + ReactiveSecurityContextHolder securityContextHolder; + + @Mock + SecurityContext securityContext; + + @Mock + Authentication authentication; + + @Mock + RbacUser user; + + @BeforeEach + void setUp() { + // Mock roles + RoleBasedAccessControlProperties properties = mock(); + + Role memeLordsRole = new Role(); + memeLordsRole.setClusters(List.of("local")); + memeLordsRole.setName("memeLords"); + List roles = List.of( + memeLordsRole + ); + when(properties.getRoles()).thenReturn(roles); + ReflectionTestUtils.setField(accessControlService, "properties", properties); + + // Mock security context + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(user); + } + + public void withSecurityContext(Runnable runnable) { + try (MockedStatic ctxHolder = Mockito.mockStatic( + ReactiveSecurityContextHolder.class)) { + // Mock static method to get security context + ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); + runnable.run(); + } + } + + @Test + void validateAccess() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of("memelords")); + AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); + when(mockedResource.isAccessible(any())).thenReturn(true); + var accessContext = new AccessContext("local", List.of( + mockedResource + ), "op", "params"); + + Mono voidMono = accessControlService.validateAccess(accessContext); + StepVerifier.create(voidMono) + .expectComplete() + .verify(); + }); + } + + @Test + void validateAccess_deniedWrongGroup() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of("otherGroup")); // wrong group + AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); + when(mockedResource.isAccessible(any())).thenReturn(true); + var accessContext = new AccessContext("local", List.of( + mockedResource + ), "op", "params"); + + Mono voidMono = accessControlService.validateAccess(accessContext); + StepVerifier.create(voidMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + @Test + void validateAccess_deniedWrongCluster() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of("memelords")); + AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); + when(mockedResource.isAccessible(any())).thenReturn(true); + var accessContext = new AccessContext("prod", // wrong cluster + List.of( + mockedResource + ), "op", "params"); + + Mono voidMono = accessControlService.validateAccess(accessContext); + StepVerifier.create(voidMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + @Test + void validateAccess_deniedResourceNotAcessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of("memelords")); + AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); + when(mockedResource.isAccessible(any())).thenReturn(false); // resource not acessible + var accessContext = new AccessContext("local", List.of( + mockedResource + ), "op", "params"); + + Mono voidMono = accessControlService.validateAccess(accessContext); + StepVerifier.create(voidMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + +} diff --git a/api/src/test/resources/application-test.yml b/api/src/test/resources/application-test.yml index 5709d1452..ff2bcd0f5 100644 --- a/api/src/test/resources/application-test.yml +++ b/api/src/test/resources/application-test.yml @@ -2,4 +2,4 @@ spring: jmx: enabled: true auth: - type: DISABLED \ No newline at end of file + type: DISABLED From f868570eaed78efa87ec5dcd7ce0d5cfd9410dd4 Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Sun, 12 Jan 2025 10:53:46 -0300 Subject: [PATCH 2/8] rbac enabled tests --- .../AccessControlServiceRbacEnabledTest.java | 294 ++++++++++++++++++ .../rbac/AccessControlServiceTest.java | 169 ---------- .../ui/service/rbac/MockedRbacUtils.java | 99 ++++++ 3 files changed, 393 insertions(+), 169 deletions(-) create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java delete mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java new file mode 100644 index 000000000..57264190e --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java @@ -0,0 +1,294 @@ +package io.kafbat.ui.service.rbac; + +import static io.kafbat.ui.service.rbac.MockedRbacUtils.ADMIN_ROLE; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.CONNECT_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.CONSUMER_GROUP_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.DEV_CLUSTER; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.DEV_ROLE; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.PROD_CLUSTER; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.SCHEMA_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.TOPIC_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.getAccessContext; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.config.auth.RbacUser; +import io.kafbat.ui.config.auth.RoleBasedAccessControlProperties; +import io.kafbat.ui.model.ClusterDTO; +import io.kafbat.ui.model.ConnectDTO; +import io.kafbat.ui.model.InternalTopic; +import io.kafbat.ui.model.rbac.AccessContext; +import io.kafbat.ui.model.rbac.Role; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.util.ReflectionTestUtils; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +/** + * Test cases for AccessControlService when RBAC is enabled. + */ +class AccessControlServiceRbacEnabledTest extends AbstractIntegrationTest { + + @Autowired + AccessControlService accessControlService; + + @Mock + SecurityContext securityContext; + + @Mock + Authentication authentication; + + @Mock + RbacUser user; + + @BeforeEach + void setUp() { + // Mock roles + List roles = List.of( + MockedRbacUtils.getAdminRole(), + MockedRbacUtils.getDevRole() + ); + RoleBasedAccessControlProperties properties = mock(); + when(properties.getRoles()).thenReturn(roles); + + ReflectionTestUtils.setField(accessControlService, "properties", properties); + ReflectionTestUtils.setField(accessControlService, "rbacEnabled", true); + + // Mock security context + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(user); + } + + public void withSecurityContext(Runnable runnable) { + try (MockedStatic ctxHolder = Mockito.mockStatic( + ReactiveSecurityContextHolder.class)) { + // Mock static method to get security context + ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); + runnable.run(); + } + } + + @Test + void validateAccess() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ADMIN_ROLE)); + AccessContext context = getAccessContext(PROD_CLUSTER, true); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + @Test + void validateAccess_deniedCluster() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + AccessContext context = getAccessContext(PROD_CLUSTER, true); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + @Test + void validateAccess_deniedResourceNotAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ADMIN_ROLE)); + AccessContext context = getAccessContext(PROD_CLUSTER, false); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + @Test + void isClusterAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ADMIN_ROLE)); + ClusterDTO clusterDto = new ClusterDTO(); + clusterDto.setName(PROD_CLUSTER); + Mono clusterAccessibleMono = accessControlService.isClusterAccessible(clusterDto); + StepVerifier.create(clusterAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isClusterAccessible_deniedCluster() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + ClusterDTO clusterDto = new ClusterDTO(); + clusterDto.setName(PROD_CLUSTER); + Mono clusterAccessibleMono = accessControlService.isClusterAccessible(clusterDto); + StepVerifier.create(clusterAccessibleMono) + .expectNext(false) + .expectComplete() + .verify(); + }); + } + + @Test + void filterViewableTopics() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + ClusterDTO clusterDto = new ClusterDTO(); + clusterDto.setName(DEV_CLUSTER); + List topics = List.of( + InternalTopic.builder() + .name(TOPIC_NAME) + .build() + ); + Mono> filterTopicsMono = accessControlService.filterViewableTopics(topics, DEV_CLUSTER); + StepVerifier.create(filterTopicsMono) + .expectNextMatches(responseTopics -> responseTopics.stream().anyMatch(t -> t.getName().equals(TOPIC_NAME))) + .expectComplete() + .verify(); + }); + } + + @Test + void filterViewableTopics_notAccessibleTopic() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + ClusterDTO clusterDto = new ClusterDTO(); + clusterDto.setName(DEV_CLUSTER); + List topics = List.of( + InternalTopic.builder() + .name("some other topic") + .build() + ); + Mono> filterTopicsMono = accessControlService.filterViewableTopics(topics, DEV_CLUSTER); + StepVerifier.create(filterTopicsMono) + .expectNextMatches(List::isEmpty) + .expectComplete() + .verify(); + }); + } + + @Test + void isConsumerGroupAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isConsumerGroupAccessible(CONSUMER_GROUP_NAME, DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isConsumerGroupAccessible_notAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isConsumerGroupAccessible("SOME OTHER CONSUMER", DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(false) + .expectComplete() + .verify(); + }); + } + + @Test + void isSchemaAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isSchemaAccessible(SCHEMA_NAME, DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isSchemaAccessible_notAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isSchemaAccessible("SOME OTHER SCHEMA", DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(false) + .expectComplete() + .verify(); + }); + } + + @Test + void isConnectAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isConnectAccessible(CONNECT_NAME, DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isConnectAccessible_notAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isConnectAccessible("SOME OTHER CONNECT", DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(false) + .expectComplete() + .verify(); + }); + } + + @Test + void isConnectAccessibleDto() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + ConnectDTO connectDto = ConnectDTO.builder() + .name(CONNECT_NAME) + .build(); + Mono consumerGroupAccessibleMono = + accessControlService.isConnectAccessible(connectDto, DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isConnectAccessibleDto_notAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + ConnectDTO connectDto = ConnectDTO.builder() + .name("SOME OTHER CONNECT") + .build(); + Mono consumerGroupAccessibleMono = + accessControlService.isConnectAccessible(connectDto, DEV_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(false) + .expectComplete() + .verify(); + }); + } + +} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java deleted file mode 100644 index 39cce976b..000000000 --- a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceTest.java +++ /dev/null @@ -1,169 +0,0 @@ -package io.kafbat.ui.service.rbac; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import io.kafbat.ui.AbstractIntegrationTest; -import io.kafbat.ui.config.auth.RbacUser; -import io.kafbat.ui.config.auth.RoleBasedAccessControlProperties; -import io.kafbat.ui.model.rbac.AccessContext; -import java.util.List; -import io.kafbat.ui.model.rbac.Role; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.ReactiveSecurityContextHolder; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.util.ReflectionTestUtils; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -//@ContextConfiguration(initializers = {AccessControlServiceTest.PropertiesInitializer.class}) -class AccessControlServiceTest extends AbstractIntegrationTest { - -// public static class PropertiesInitializer extends AbstractIntegrationTest.Initializer -// implements ApplicationContextInitializer { - -// @Override -// public void initialize(ConfigurableApplicationContext applicationContext) { -// System.setProperty("rbac.roles[0].name", "memelords"); -// System.setProperty("rbac.roles[0].clusters[0]", "local"); -// -// System.setProperty("rbac.roles[0].subjects[0].provider", "oauth_google"); -// System.setProperty("rbac.roles[0].subjects[0].type", "domain"); -// System.setProperty("rbac.roles[0].subjects[0].value", "katbat.dev"); -// -// System.setProperty("rbac.roles[0].subjects[1].provider", "oauth_google"); -// System.setProperty("rbac.roles[0].subjects[1].type", "user"); -// System.setProperty("rbac.roles[0].subjects[1].value", "name@kafbat.dev"); -// -// System.setProperty("rbac.roles[0].permissions[0].resource", "applicationconfig"); -// System.setProperty("rbac.roles[0].permissions[0].actions", "all"); -// -// super.initialize(applicationContext); -// } -// } - - @Autowired - AccessControlService accessControlService; - - @Mock - ReactiveSecurityContextHolder securityContextHolder; - - @Mock - SecurityContext securityContext; - - @Mock - Authentication authentication; - - @Mock - RbacUser user; - - @BeforeEach - void setUp() { - // Mock roles - RoleBasedAccessControlProperties properties = mock(); - - Role memeLordsRole = new Role(); - memeLordsRole.setClusters(List.of("local")); - memeLordsRole.setName("memeLords"); - List roles = List.of( - memeLordsRole - ); - when(properties.getRoles()).thenReturn(roles); - ReflectionTestUtils.setField(accessControlService, "properties", properties); - - // Mock security context - when(securityContext.getAuthentication()).thenReturn(authentication); - when(authentication.getPrincipal()).thenReturn(user); - } - - public void withSecurityContext(Runnable runnable) { - try (MockedStatic ctxHolder = Mockito.mockStatic( - ReactiveSecurityContextHolder.class)) { - // Mock static method to get security context - ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); - runnable.run(); - } - } - - @Test - void validateAccess() { - withSecurityContext(() -> { - when(user.groups()).thenReturn(List.of("memelords")); - AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); - when(mockedResource.isAccessible(any())).thenReturn(true); - var accessContext = new AccessContext("local", List.of( - mockedResource - ), "op", "params"); - - Mono voidMono = accessControlService.validateAccess(accessContext); - StepVerifier.create(voidMono) - .expectComplete() - .verify(); - }); - } - - @Test - void validateAccess_deniedWrongGroup() { - withSecurityContext(() -> { - when(user.groups()).thenReturn(List.of("otherGroup")); // wrong group - AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); - when(mockedResource.isAccessible(any())).thenReturn(true); - var accessContext = new AccessContext("local", List.of( - mockedResource - ), "op", "params"); - - Mono voidMono = accessControlService.validateAccess(accessContext); - StepVerifier.create(voidMono) - .expectErrorMatches(e -> e instanceof AccessDeniedException) - .verify(); - }); - } - - @Test - void validateAccess_deniedWrongCluster() { - withSecurityContext(() -> { - when(user.groups()).thenReturn(List.of("memelords")); - AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); - when(mockedResource.isAccessible(any())).thenReturn(true); - var accessContext = new AccessContext("prod", // wrong cluster - List.of( - mockedResource - ), "op", "params"); - - Mono voidMono = accessControlService.validateAccess(accessContext); - StepVerifier.create(voidMono) - .expectErrorMatches(e -> e instanceof AccessDeniedException) - .verify(); - }); - } - - @Test - void validateAccess_deniedResourceNotAcessible() { - withSecurityContext(() -> { - when(user.groups()).thenReturn(List.of("memelords")); - AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); - when(mockedResource.isAccessible(any())).thenReturn(false); // resource not acessible - var accessContext = new AccessContext("local", List.of( - mockedResource - ), "op", "params"); - - Mono voidMono = accessControlService.validateAccess(accessContext); - StepVerifier.create(voidMono) - .expectErrorMatches(e -> e instanceof AccessDeniedException) - .verify(); - }); - } - -} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java b/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java new file mode 100644 index 000000000..f9e8bb3c9 --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java @@ -0,0 +1,99 @@ +package io.kafbat.ui.service.rbac; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.model.rbac.AccessContext; +import io.kafbat.ui.model.rbac.Permission; +import io.kafbat.ui.model.rbac.Resource; +import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.permission.ConnectAction; +import io.kafbat.ui.model.rbac.permission.ConsumerGroupAction; +import io.kafbat.ui.model.rbac.permission.SchemaAction; +import io.kafbat.ui.model.rbac.permission.TopicAction; +import java.util.List; + +public class MockedRbacUtils { + + public static final String ADMIN_ROLE = "admin_role"; + public static final String DEV_ROLE = "dev_role"; + + public static final String PROD_CLUSTER = "prod"; + public static final String DEV_CLUSTER = "dev"; + + public static final String TOPIC_NAME = "aTopic"; + public static final String CONSUMER_GROUP_NAME = "aConsumerGroup"; + public static final String SCHEMA_NAME = "aSchema"; + public static final String CONNECT_NAME = "aConnect"; + + /** + * All actions to Resource.APPLICATIONCONFIG for dev and prod clusters. + * + * @return admin role + */ + public static Role getAdminRole() { + Role role = new Role(); + role.setName(ADMIN_ROLE); + role.setClusters(List.of(DEV_CLUSTER, PROD_CLUSTER)); + Permission applicationConfigPerm = new Permission(); + applicationConfigPerm.setResource(Resource.APPLICATIONCONFIG.name()); + applicationConfigPerm.setActions(List.of("all")); + List permissions = List.of( + applicationConfigPerm + ); + role.setPermissions(permissions); + role.validate(); + return role; + } + + /** + * View actions to topic, consumer, schema and connect + * + * @return admin role + */ + public static Role getDevRole() { + Role role = new Role(); + role.setName(DEV_ROLE); + role.setClusters(List.of(DEV_CLUSTER)); + + Permission topicViewPermission = new Permission(); + topicViewPermission.setResource(Resource.TOPIC.name()); + topicViewPermission.setActions(List.of(TopicAction.VIEW.name())); + topicViewPermission.setValue(TOPIC_NAME); + + Permission consumerGroupPermission = new Permission(); + consumerGroupPermission.setResource(Resource.CONSUMER.name()); + consumerGroupPermission.setActions(List.of(ConsumerGroupAction.VIEW.name())); + consumerGroupPermission.setValue(CONSUMER_GROUP_NAME); + + Permission schemaPermission = new Permission(); + schemaPermission.setResource(Resource.SCHEMA.name()); + schemaPermission.setActions(List.of(SchemaAction.VIEW.name())); + schemaPermission.setValue(SCHEMA_NAME); + + Permission connectPermission = new Permission(); + connectPermission.setResource(Resource.CONNECT.name()); + connectPermission.setActions(List.of(ConnectAction.VIEW.name())); + connectPermission.setValue(CONNECT_NAME); + + List permissions = List.of( + topicViewPermission, + consumerGroupPermission, + schemaPermission, + connectPermission + ); + role.setPermissions(permissions); + role.validate(); + return role; + } + + public static AccessContext getAccessContext(String cluster, Boolean resourceAccessible) { + AccessContext.ResourceAccess mockedResource = mock(AccessContext.ResourceAccess.class); + when(mockedResource.isAccessible(any())).thenReturn(resourceAccessible); + return new AccessContext(cluster, List.of( + mockedResource + ), "op", "params"); + } + +} From 1d5ca3b5183004ddf2612cb487b8996a4773c11a Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Sun, 12 Jan 2025 11:23:31 -0300 Subject: [PATCH 3/8] added more test, closes #729 --- ...ccessControlServiceInitPropertiesTest.java | 59 ++++++ .../AccessControlServiceRbacDisabledTest.java | 173 ++++++++++++++++++ .../AccessControlServiceRbacEnabledTest.java | 15 +- .../ui/service/rbac/MockedRbacUtils.java | 2 +- 4 files changed, 244 insertions(+), 5 deletions(-) create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacDisabledTest.java diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java new file mode 100644 index 000000000..81f261e23 --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java @@ -0,0 +1,59 @@ +package io.kafbat.ui.service.rbac; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.service.rbac.extractor.GoogleAuthorityExtractor; +import io.kafbat.ui.service.rbac.extractor.ProviderAuthorityExtractor; +import java.util.Set; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; + +/** + * Test case for Properties initializer. + */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +@ContextConfiguration(initializers = AccessControlServiceInitPropertiesTest.PropertiesInitializer.class) +class AccessControlServiceInitPropertiesTest extends AbstractIntegrationTest { + + public static class PropertiesInitializer extends AbstractIntegrationTest.Initializer + implements ApplicationContextInitializer { + + @Override + public void initialize(@NotNull ConfigurableApplicationContext applicationContext) { + System.setProperty("rbac.roles[0].name", "memelords"); + System.setProperty("rbac.roles[0].clusters[0]", "local"); + + System.setProperty("rbac.roles[0].subjects[0].provider", "oauth_google"); + System.setProperty("rbac.roles[0].subjects[0].type", "domain"); + System.setProperty("rbac.roles[0].subjects[0].value", "katbat.dev"); + + System.setProperty("rbac.roles[0].subjects[1].provider", "oauth_google"); + System.setProperty("rbac.roles[0].subjects[1].type", "user"); + System.setProperty("rbac.roles[0].subjects[1].value", "name@kafbat.dev"); + + System.setProperty("rbac.roles[0].permissions[0].resource", "applicationconfig"); + System.setProperty("rbac.roles[0].permissions[0].actions", "all"); + } + } + + @Autowired + AccessControlService accessControlService; + + @Test + void rbacEnabled() { + assertTrue(accessControlService.isRbacEnabled()); + + Set oauthExtractors = accessControlService.getOauthExtractors(); + assertThat(oauthExtractors) + .hasSize(1) + .anyMatch(ext -> ext instanceof GoogleAuthorityExtractor); + } + +} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacDisabledTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacDisabledTest.java new file mode 100644 index 000000000..199771a78 --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacDisabledTest.java @@ -0,0 +1,173 @@ +package io.kafbat.ui.service.rbac; + +import static io.kafbat.ui.service.rbac.MockedRbacUtils.CONNECT_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.CONSUMER_GROUP_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.DEV_ROLE; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.PROD_CLUSTER; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.SCHEMA_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.TOPIC_NAME; +import static io.kafbat.ui.service.rbac.MockedRbacUtils.getAccessContext; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.config.auth.RbacUser; +import io.kafbat.ui.model.ClusterDTO; +import io.kafbat.ui.model.ConnectDTO; +import io.kafbat.ui.model.InternalTopic; +import io.kafbat.ui.model.rbac.AccessContext; +import io.kafbat.ui.model.rbac.Role; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.annotation.DirtiesContext; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +/** + * Test cases for AccessControlService when RBAC is disabled. + * Using PROD cluster and user DEV role for all tests. + */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +class AccessControlServiceRbacDisabledTest extends AbstractIntegrationTest { + + @Autowired + AccessControlService accessControlService; + + @Mock + SecurityContext securityContext; + + @Mock + Authentication authentication; + + @Mock + RbacUser user; + + @BeforeEach + void setUp() { + // Mock security context + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(user); + } + + public void withSecurityContext(Runnable runnable) { + try (MockedStatic ctxHolder = Mockito.mockStatic( + ReactiveSecurityContextHolder.class)) { + // Mock static method to get security context + ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); + runnable.run(); + } + } + + @Test + void validateAccess() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + AccessContext context = getAccessContext(PROD_CLUSTER, true); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + @Test + void isClusterAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + ClusterDTO clusterDto = new ClusterDTO(); + clusterDto.setName(PROD_CLUSTER); + Mono clusterAccessibleMono = accessControlService.isClusterAccessible(clusterDto); + StepVerifier.create(clusterAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void filterViewableTopics() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + List topics = List.of( + InternalTopic.builder() + .name(TOPIC_NAME) + .build() + ); + Mono> filterTopicsMono = accessControlService.filterViewableTopics(topics, PROD_CLUSTER); + StepVerifier.create(filterTopicsMono) + .expectNextMatches(responseTopics -> responseTopics.stream().anyMatch(t -> t.getName().equals(TOPIC_NAME))) + .expectComplete() + .verify(); + }); + } + + @Test + void isConsumerGroupAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isConsumerGroupAccessible(CONSUMER_GROUP_NAME, PROD_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isSchemaAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isSchemaAccessible(SCHEMA_NAME, PROD_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isConnectAccessible() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + Mono consumerGroupAccessibleMono = + accessControlService.isConnectAccessible(CONNECT_NAME, PROD_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void isConnectAccessibleDto() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(DEV_ROLE)); + ConnectDTO connectDto = ConnectDTO.builder() + .name(CONNECT_NAME) + .build(); + Mono consumerGroupAccessibleMono = + accessControlService.isConnectAccessible(connectDto, PROD_CLUSTER); + StepVerifier.create(consumerGroupAccessibleMono) + .expectNext(true) + .expectComplete() + .verify(); + }); + } + + @Test + void getRoles() { + List roles = accessControlService.getRoles(); + assertThat(roles).isEmpty(); + } + +} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java index 57264190e..b00d4e7d4 100644 --- a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java +++ b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java @@ -9,6 +9,7 @@ import static io.kafbat.ui.service.rbac.MockedRbacUtils.SCHEMA_NAME; import static io.kafbat.ui.service.rbac.MockedRbacUtils.TOPIC_NAME; import static io.kafbat.ui.service.rbac.MockedRbacUtils.getAccessContext; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -31,6 +32,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.util.ReflectionTestUtils; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -38,6 +40,7 @@ /** * Test cases for AccessControlService when RBAC is enabled. */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) class AccessControlServiceRbacEnabledTest extends AbstractIntegrationTest { @Autowired @@ -147,8 +150,6 @@ void isClusterAccessible_deniedCluster() { void filterViewableTopics() { withSecurityContext(() -> { when(user.groups()).thenReturn(List.of(DEV_ROLE)); - ClusterDTO clusterDto = new ClusterDTO(); - clusterDto.setName(DEV_CLUSTER); List topics = List.of( InternalTopic.builder() .name(TOPIC_NAME) @@ -166,8 +167,6 @@ void filterViewableTopics() { void filterViewableTopics_notAccessibleTopic() { withSecurityContext(() -> { when(user.groups()).thenReturn(List.of(DEV_ROLE)); - ClusterDTO clusterDto = new ClusterDTO(); - clusterDto.setName(DEV_CLUSTER); List topics = List.of( InternalTopic.builder() .name("some other topic") @@ -291,4 +290,12 @@ void isConnectAccessibleDto_notAccessible() { }); } + @Test + void getRoles() { + List roles = accessControlService.getRoles(); + assertThat(roles).hasSize(2) + .anyMatch(role -> role.getName().equals(DEV_ROLE)) + .anyMatch(role -> role.getName().equals(ADMIN_ROLE)); + } + } diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java b/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java index f9e8bb3c9..f4c6dc5e7 100644 --- a/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java +++ b/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java @@ -48,7 +48,7 @@ public static Role getAdminRole() { } /** - * View actions to topic, consumer, schema and connect + * View actions to topic, consumer, schema and connect. * * @return admin role */ From fa99b398c5e931639e26cccbe172e88b43220dd7 Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Mon, 13 Jan 2025 22:26:02 -0300 Subject: [PATCH 4/8] added test cases for issues --- ...ccessControlServiceInitPropertiesTest.java | 59 ----- .../service/rbac/RbacClusterConfigTest.java | 110 ++++++++++ .../service/rbac/RbacClusterMistakenTest.java | 207 ++++++++++++++++++ .../ui/service/rbac/RbacEditTopicTest.java | 130 +++++++++++ .../service/rbac/RbacTopicCreationTest.java | 151 +++++++++++++ 5 files changed, 598 insertions(+), 59 deletions(-) delete mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterConfigTest.java create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java create mode 100644 api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java deleted file mode 100644 index 81f261e23..000000000 --- a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceInitPropertiesTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.kafbat.ui.service.rbac; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.kafbat.ui.AbstractIntegrationTest; -import io.kafbat.ui.service.rbac.extractor.GoogleAuthorityExtractor; -import io.kafbat.ui.service.rbac.extractor.ProviderAuthorityExtractor; -import java.util.Set; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; - -/** - * Test case for Properties initializer. - */ -@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) -@ContextConfiguration(initializers = AccessControlServiceInitPropertiesTest.PropertiesInitializer.class) -class AccessControlServiceInitPropertiesTest extends AbstractIntegrationTest { - - public static class PropertiesInitializer extends AbstractIntegrationTest.Initializer - implements ApplicationContextInitializer { - - @Override - public void initialize(@NotNull ConfigurableApplicationContext applicationContext) { - System.setProperty("rbac.roles[0].name", "memelords"); - System.setProperty("rbac.roles[0].clusters[0]", "local"); - - System.setProperty("rbac.roles[0].subjects[0].provider", "oauth_google"); - System.setProperty("rbac.roles[0].subjects[0].type", "domain"); - System.setProperty("rbac.roles[0].subjects[0].value", "katbat.dev"); - - System.setProperty("rbac.roles[0].subjects[1].provider", "oauth_google"); - System.setProperty("rbac.roles[0].subjects[1].type", "user"); - System.setProperty("rbac.roles[0].subjects[1].value", "name@kafbat.dev"); - - System.setProperty("rbac.roles[0].permissions[0].resource", "applicationconfig"); - System.setProperty("rbac.roles[0].permissions[0].actions", "all"); - } - } - - @Autowired - AccessControlService accessControlService; - - @Test - void rbacEnabled() { - assertTrue(accessControlService.isRbacEnabled()); - - Set oauthExtractors = accessControlService.getOauthExtractors(); - assertThat(oauthExtractors) - .hasSize(1) - .anyMatch(ext -> ext instanceof GoogleAuthorityExtractor); - } - -} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterConfigTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterConfigTest.java new file mode 100644 index 000000000..69c92f4f4 --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterConfigTest.java @@ -0,0 +1,110 @@ +package io.kafbat.ui.service.rbac; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.config.auth.RbacUser; +import io.kafbat.ui.config.auth.RoleBasedAccessControlProperties; +import io.kafbat.ui.model.rbac.AccessContext; +import io.kafbat.ui.model.rbac.Permission; +import io.kafbat.ui.model.rbac.Resource; +import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.permission.ClusterConfigAction; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.util.ReflectionTestUtils; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +/** + * Test cases for issue #461. + * Sets the role to any cluster ".*" + */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +class RbacClusterConfigTest extends AbstractIntegrationTest { + + public static final String ROLE_NAME = "cluster_config_role"; + + @Autowired + AccessControlService accessControlService; + + @Mock + SecurityContext securityContext; + + @Mock + Authentication authentication; + + @Mock + RbacUser user; + + @BeforeEach + void setUp() { + // Mock roles + List roles = List.of( + getClusterConfigRole() + ); + RoleBasedAccessControlProperties properties = mock(); + when(properties.getRoles()).thenReturn(roles); + + ReflectionTestUtils.setField(accessControlService, "properties", properties); + ReflectionTestUtils.setField(accessControlService, "rbacEnabled", true); + + // Mock security context + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(user); + } + + public void withSecurityContext(Runnable runnable) { + try (MockedStatic ctxHolder = Mockito.mockStatic( + ReactiveSecurityContextHolder.class)) { + // Mock static method to get security context + ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); + runnable.run(); + } + } + + @Test + @Disabled("expected to work after issue #461 is resolved") + void validateAccess_clusterConfigAll_propertiesAllCluster() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster("prod") + .clusterConfigActions(ClusterConfigAction.EDIT) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + public static Role getClusterConfigRole() { + Role role = new Role(); + role.setName(ROLE_NAME); + role.setClusters(List.of(".*")); // setting role for any cluster + + Permission permission = new Permission(); + permission.setResource(Resource.CLUSTERCONFIG.name()); + permission.setActions(List.of("all")); + + List permissions = List.of( + permission + ); + role.setPermissions(permissions); + role.validate(); + return role; + } + +} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java new file mode 100644 index 000000000..074563244 --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java @@ -0,0 +1,207 @@ +package io.kafbat.ui.service.rbac; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.config.auth.RbacUser; +import io.kafbat.ui.config.auth.RoleBasedAccessControlProperties; +import io.kafbat.ui.model.rbac.AccessContext; +import io.kafbat.ui.model.rbac.Permission; +import io.kafbat.ui.model.rbac.Resource; +import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.permission.AclAction; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.util.ReflectionTestUtils; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +/** + * Test cases for issue #274. + */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +class RbacClusterMistakenTest extends AbstractIntegrationTest { + + public static final String ADMIN_ROLE_NAME = "Admin Roles"; + + public static final String DEV_CLUSTER_ADM = "DEV"; + public static final String TST_CLUSTER_ADM = "TST"; + public static final String UAT_CLUSTER_ADM = "UAT"; + public static final String LAB_CLUSTER_ALL = "LAB"; + public static final String LAB_ROLE_NAME = "LAB for ALL"; + + @Autowired + AccessControlService accessControlService; + + @Mock + SecurityContext securityContext; + + @Mock + Authentication authentication; + + @Mock + RbacUser user; + + @BeforeEach + void setUp() { + // Mock roles + List roles = List.of( + getAdminRole(), + getDevRole() + ); + RoleBasedAccessControlProperties properties = mock(); + when(properties.getRoles()).thenReturn(roles); + + ReflectionTestUtils.setField(accessControlService, "properties", properties); + ReflectionTestUtils.setField(accessControlService, "rbacEnabled", true); + + // Mock security context + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(user); + } + + public void withSecurityContext(Runnable runnable) { + try (MockedStatic ctxHolder = Mockito.mockStatic( + ReactiveSecurityContextHolder.class)) { + // Mock static method to get security context + ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); + runnable.run(); + } + } + + /** + * Anyone editing LAB. + */ + @Test + void validateAccess() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(LAB_ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(LAB_CLUSTER_ALL) + .aclActions(AclAction.EDIT) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + /** + * Admin with both roles editing LAB. + */ + @Test + void validateAccess_bothRoles() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ADMIN_ROLE_NAME, LAB_ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(LAB_CLUSTER_ALL) + .aclActions(AclAction.EDIT) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + /** + * Anyone editing Dev cluster, denied. + */ + @Test + void validateAccess_Denied() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(LAB_ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(DEV_CLUSTER_ADM) + .aclActions(AclAction.EDIT) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + /** + * Admin editing dev cluster, denied. + */ + @Test + void validateAccess_DeniedAdminEditing() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ADMIN_ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(DEV_CLUSTER_ADM) + .aclActions(AclAction.EDIT) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + /** + * Admin viewing Dev cluster, allowed. + */ + @Test + void validateAccess_viewAllowedAdmin() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ADMIN_ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(DEV_CLUSTER_ADM) + .aclActions(AclAction.VIEW) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + private Role getAdminRole() { + Role role = new Role(); + role.setName(ADMIN_ROLE_NAME); + role.setClusters(List.of(DEV_CLUSTER_ADM, TST_CLUSTER_ADM, UAT_CLUSTER_ADM)); + + Permission permission = new Permission(); + permission.setResource(Resource.ACL.name()); + permission.setActions(List.of(AclAction.VIEW.name())); + + List permissions = List.of( + permission + ); + role.setPermissions(permissions); + role.validate(); + return role; + } + + private Role getDevRole() { + Role role = new Role(); + role.setName(LAB_ROLE_NAME); + role.setClusters(List.of(LAB_CLUSTER_ALL)); + + Permission permission = new Permission(); + permission.setResource(Resource.ACL.name()); + permission.setActions(List.of(AclAction.VIEW.name(), AclAction.EDIT.name())); + + List permissions = List.of( + permission + ); + role.setPermissions(permissions); + role.validate(); + return role; + } + +} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java new file mode 100644 index 000000000..1a2a66d00 --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java @@ -0,0 +1,130 @@ +package io.kafbat.ui.service.rbac; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.config.auth.RbacUser; +import io.kafbat.ui.config.auth.RoleBasedAccessControlProperties; +import io.kafbat.ui.model.rbac.AccessContext; +import io.kafbat.ui.model.rbac.Permission; +import io.kafbat.ui.model.rbac.Resource; +import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.permission.TopicAction; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.util.ReflectionTestUtils; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +/** + * Test cases for issue #260. + * The role has permissions to delete messages from any topic + */ +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +class RbacEditTopicTest extends AbstractIntegrationTest { + + public static final String ROLE_NAME = "role-name-ro"; + public static final String CLUSTER_NAME = "dev"; + + @Autowired + AccessControlService accessControlService; + + @Mock + SecurityContext securityContext; + + @Mock + Authentication authentication; + + @Mock + RbacUser user; + + @BeforeEach + void setUp() { + // Mock roles + List roles = List.of( + getEditTopicTestRole() + ); + RoleBasedAccessControlProperties properties = mock(); + when(properties.getRoles()).thenReturn(roles); + + ReflectionTestUtils.setField(accessControlService, "properties", properties); + ReflectionTestUtils.setField(accessControlService, "rbacEnabled", true); + + // Mock security context + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(user); + } + + public void withSecurityContext(Runnable runnable) { + try (MockedStatic ctxHolder = Mockito.mockStatic( + ReactiveSecurityContextHolder.class)) { + // Mock static method to get security context + ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); + runnable.run(); + } + } + + @Test + void validateAccess() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(CLUSTER_NAME) + .topicActions("inventorytopic", TopicAction.MESSAGES_DELETE) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + @Test + void validateAccess_deniedRole() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of("anotherRole")); + AccessContext context = AccessContext.builder() + .cluster(CLUSTER_NAME) + .topicActions("inventorytopic", TopicAction.MESSAGES_DELETE) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + public static Role getEditTopicTestRole() { + Role role = new Role(); + role.setName(ROLE_NAME); + role.setClusters(List.of(CLUSTER_NAME)); + + Permission topicPermissionTestPrefix = new Permission(); + topicPermissionTestPrefix.setResource(Resource.TOPIC.name()); + topicPermissionTestPrefix.setActions(List.of( + TopicAction.VIEW.name(), + TopicAction.MESSAGES_READ.name(), + TopicAction.MESSAGES_DELETE.name() + )); + topicPermissionTestPrefix.setValue(".*"); + + List permissions = List.of( + topicPermissionTestPrefix + ); + role.setPermissions(permissions); + role.validate(); + return role; + } + +} diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java new file mode 100644 index 000000000..dfb73d905 --- /dev/null +++ b/api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java @@ -0,0 +1,151 @@ +package io.kafbat.ui.service.rbac; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.kafbat.ui.AbstractIntegrationTest; +import io.kafbat.ui.config.auth.RbacUser; +import io.kafbat.ui.config.auth.RoleBasedAccessControlProperties; +import io.kafbat.ui.model.rbac.AccessContext; +import io.kafbat.ui.model.rbac.Permission; +import io.kafbat.ui.model.rbac.Resource; +import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.permission.TopicAction; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.util.ReflectionTestUtils; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +/** + * Test cases for issues #76. + * User is allowed to create topic based only on pattern "test-.*" + */ +@ActiveProfiles("topiccreation") +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +class RbacTopicCreationTest extends AbstractIntegrationTest { + + public static final String ROLE_NAME = "dev_role"; + public static final String CLUSTER_NAME = "dev"; + @Autowired + AccessControlService accessControlService; + + @Mock + SecurityContext securityContext; + + @Mock + Authentication authentication; + + @Mock + RbacUser user; + + @BeforeEach + void setUp() { + // Mock roles + List roles = List.of( + getRole() + ); + RoleBasedAccessControlProperties properties = mock(); + when(properties.getRoles()).thenReturn(roles); + + ReflectionTestUtils.setField(accessControlService, "properties", properties); + ReflectionTestUtils.setField(accessControlService, "rbacEnabled", true); + + // Mock security context + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(user); + } + + public void withSecurityContext(Runnable runnable) { + try (MockedStatic ctxHolder = Mockito.mockStatic( + ReactiveSecurityContextHolder.class)) { + // Mock static method to get security context + ctxHolder.when(ReactiveSecurityContextHolder::getContext).thenReturn(Mono.just(securityContext)); + runnable.run(); + } + } + + /** + * Create a "test-" prefixed topic, allowed. + */ + @Test + void validateAccess() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(CLUSTER_NAME) + .topicActions("test-topic", TopicAction.CREATE) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectComplete() + .verify(); + }); + } + + /** + * Create a not "test-" prefixed topic, not allowed. + */ + @Test + void validateAccess_accessDenied() { + withSecurityContext(() -> { + when(user.groups()).thenReturn(List.of(ROLE_NAME)); + AccessContext context = AccessContext.builder() + .cluster(CLUSTER_NAME) + .topicActions("another-topic", TopicAction.CREATE) + .build(); + Mono validateAccessMono = accessControlService.validateAccess(context); + StepVerifier.create(validateAccessMono) + .expectErrorMatches(e -> e instanceof AccessDeniedException) + .verify(); + }); + } + + public static Role getRole() { + Role role = new Role(); + role.setName(ROLE_NAME); + role.setClusters(List.of(CLUSTER_NAME)); + + Permission testTopicsPermission = new Permission(); + testTopicsPermission.setResource(Resource.TOPIC.name()); + testTopicsPermission.setActions(List.of( + TopicAction.VIEW.name(), + TopicAction.CREATE.name(), + TopicAction.EDIT.name(), + TopicAction.DELETE.name(), + TopicAction.MESSAGES_READ.name(), + TopicAction.MESSAGES_PRODUCE.name() + )); + testTopicsPermission.setValue("test-.*"); + + Permission notPrefixedTopicPermission = new Permission(); + notPrefixedTopicPermission.setResource(Resource.TOPIC.name()); + notPrefixedTopicPermission.setActions(List.of( + TopicAction.VIEW.name(), + TopicAction.MESSAGES_READ.name(), + TopicAction.MESSAGES_PRODUCE.name() + )); + notPrefixedTopicPermission.setValue("^(?!test-).*"); + + List permissions = List.of( + testTopicsPermission, + notPrefixedTopicPermission + ); + role.setPermissions(permissions); + role.validate(); + return role; + } + +} From efe4a70ec795fa4d6fcdec780aad9baf045e813a Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Wed, 15 Jan 2025 18:42:14 -0300 Subject: [PATCH 5/8] Update application-test.yml From 520591ad12fedef48645792325deeb0a092709b0 Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Wed, 15 Jan 2025 18:50:46 -0300 Subject: [PATCH 6/8] revert yml file --- api/src/test/resources/application-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/test/resources/application-test.yml b/api/src/test/resources/application-test.yml index ff2bcd0f5..5709d1452 100644 --- a/api/src/test/resources/application-test.yml +++ b/api/src/test/resources/application-test.yml @@ -2,4 +2,4 @@ spring: jmx: enabled: true auth: - type: DISABLED + type: DISABLED \ No newline at end of file From 6d3a9fc26dbcdd398ea098aa610d4c1abab9a03f Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Sat, 18 Jan 2025 11:16:38 -0300 Subject: [PATCH 7/8] fix tests for new validation --- .../io/kafbat/ui/service/rbac/MockedRbacUtils.java | 12 +++++++++++- .../ui/service/rbac/RbacClusterMistakenTest.java | 13 +++++++++++-- .../kafbat/ui/service/rbac/RbacEditTopicTest.java | 7 ++++++- .../ui/service/rbac/RbacTopicCreationTest.java | 8 ++++++-- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java b/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java index f4c6dc5e7..804c0c05c 100644 --- a/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java +++ b/api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java @@ -8,6 +8,7 @@ import io.kafbat.ui.model.rbac.Permission; import io.kafbat.ui.model.rbac.Resource; import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.Subject; import io.kafbat.ui.model.rbac.permission.ConnectAction; import io.kafbat.ui.model.rbac.permission.ConsumerGroupAction; import io.kafbat.ui.model.rbac.permission.SchemaAction; @@ -36,6 +37,11 @@ public static Role getAdminRole() { Role role = new Role(); role.setName(ADMIN_ROLE); role.setClusters(List.of(DEV_CLUSTER, PROD_CLUSTER)); + Subject sub = new Subject(); + sub.setType("group"); + sub.setProvider("ldap"); + sub.setValue("kafbat.group"); + role.setSubjects(List.of(sub)); Permission applicationConfigPerm = new Permission(); applicationConfigPerm.setResource(Resource.APPLICATIONCONFIG.name()); applicationConfigPerm.setActions(List.of("all")); @@ -56,7 +62,11 @@ public static Role getDevRole() { Role role = new Role(); role.setName(DEV_ROLE); role.setClusters(List.of(DEV_CLUSTER)); - + Subject sub = new Subject(); + sub.setType("group"); + sub.setProvider("ldap"); + sub.setValue("kafbat.group"); + role.setSubjects(List.of(sub)); Permission topicViewPermission = new Permission(); topicViewPermission.setResource(Resource.TOPIC.name()); topicViewPermission.setActions(List.of(TopicAction.VIEW.name())); diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java index 074563244..4a3a74d38 100644 --- a/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java +++ b/api/src/test/java/io/kafbat/ui/service/rbac/RbacClusterMistakenTest.java @@ -10,6 +10,7 @@ import io.kafbat.ui.model.rbac.Permission; import io.kafbat.ui.model.rbac.Resource; import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.Subject; import io.kafbat.ui.model.rbac.permission.AclAction; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -174,7 +175,11 @@ private Role getAdminRole() { Role role = new Role(); role.setName(ADMIN_ROLE_NAME); role.setClusters(List.of(DEV_CLUSTER_ADM, TST_CLUSTER_ADM, UAT_CLUSTER_ADM)); - + Subject sub = new Subject(); + sub.setType("group"); + sub.setProvider("ldap"); + sub.setValue("kafbat.group"); + role.setSubjects(List.of(sub)); Permission permission = new Permission(); permission.setResource(Resource.ACL.name()); permission.setActions(List.of(AclAction.VIEW.name())); @@ -191,7 +196,11 @@ private Role getDevRole() { Role role = new Role(); role.setName(LAB_ROLE_NAME); role.setClusters(List.of(LAB_CLUSTER_ALL)); - + Subject sub = new Subject(); + sub.setType("group"); + sub.setProvider("ldap"); + sub.setValue("kafbat.group"); + role.setSubjects(List.of(sub)); Permission permission = new Permission(); permission.setResource(Resource.ACL.name()); permission.setActions(List.of(AclAction.VIEW.name(), AclAction.EDIT.name())); diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java index 1a2a66d00..2a53f112e 100644 --- a/api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java +++ b/api/src/test/java/io/kafbat/ui/service/rbac/RbacEditTopicTest.java @@ -10,6 +10,7 @@ import io.kafbat.ui.model.rbac.Permission; import io.kafbat.ui.model.rbac.Resource; import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.Subject; import io.kafbat.ui.model.rbac.permission.TopicAction; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -109,7 +110,11 @@ public static Role getEditTopicTestRole() { Role role = new Role(); role.setName(ROLE_NAME); role.setClusters(List.of(CLUSTER_NAME)); - + Subject sub = new Subject(); + sub.setType("group"); + sub.setProvider("ldap"); + sub.setValue("kafbat.group"); + role.setSubjects(List.of(sub)); Permission topicPermissionTestPrefix = new Permission(); topicPermissionTestPrefix.setResource(Resource.TOPIC.name()); topicPermissionTestPrefix.setActions(List.of( diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java index dfb73d905..c370cbf66 100644 --- a/api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java +++ b/api/src/test/java/io/kafbat/ui/service/rbac/RbacTopicCreationTest.java @@ -1,6 +1,5 @@ package io.kafbat.ui.service.rbac; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -11,6 +10,7 @@ import io.kafbat.ui.model.rbac.Permission; import io.kafbat.ui.model.rbac.Resource; import io.kafbat.ui.model.rbac.Role; +import io.kafbat.ui.model.rbac.Subject; import io.kafbat.ui.model.rbac.permission.TopicAction; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -117,7 +117,11 @@ public static Role getRole() { Role role = new Role(); role.setName(ROLE_NAME); role.setClusters(List.of(CLUSTER_NAME)); - + Subject sub = new Subject(); + sub.setType("group"); + sub.setProvider("ldap"); + sub.setValue("kafbat.group"); + role.setSubjects(List.of(sub)); Permission testTopicsPermission = new Permission(); testTopicsPermission.setResource(Resource.TOPIC.name()); testTopicsPermission.setActions(List.of( From 4efe946c51a23d00ea563b4c5d0dc66bd1859737 Mon Sep 17 00:00:00 2001 From: Felipe Zambrin Date: Sat, 26 Apr 2025 13:46:46 -0300 Subject: [PATCH 8/8] addressing comments --- .../service/rbac/AccessControlServiceRbacEnabledTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java index b00d4e7d4..b7e307b51 100644 --- a/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java +++ b/api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceRbacEnabledTest.java @@ -259,7 +259,7 @@ void isConnectAccessible_notAccessible() { } @Test - void isConnectAccessibleDto() { + void isConnectAccessible_connectDto() { withSecurityContext(() -> { when(user.groups()).thenReturn(List.of(DEV_ROLE)); ConnectDTO connectDto = ConnectDTO.builder() @@ -275,7 +275,7 @@ void isConnectAccessibleDto() { } @Test - void isConnectAccessibleDto_notAccessible() { + void isConnectAccessible_connectDto_notAccessible() { withSecurityContext(() -> { when(user.groups()).thenReturn(List.of(DEV_ROLE)); ConnectDTO connectDto = ConnectDTO.builder() @@ -291,7 +291,7 @@ void isConnectAccessibleDto_notAccessible() { } @Test - void getRoles() { + void testGetRoles() { List roles = accessControlService.getRoles(); assertThat(roles).hasSize(2) .anyMatch(role -> role.getName().equals(DEV_ROLE))