Skip to content

Commit

Permalink
BE: RBAC: Subject type/value is unintended to be optional
Browse files Browse the repository at this point in the history
add tests
  • Loading branch information
wernerdv committed Dec 23, 2024
1 parent 8e2b511 commit 4d3d510
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public abstract class AbstractIntegrationTest {
private static final boolean IS_ARM =
System.getProperty("os.arch").contains("arm") || System.getProperty("os.arch").contains("aarch64");

private static final String CONFLUENT_PLATFORM_VERSION = IS_ARM ? "7.2.1.arm64" : "7.2.1";
public static final String CONFLUENT_PLATFORM_VERSION = IS_ARM ? "7.2.1.arm64" : "7.2.1";

public static final KafkaContainer kafka = new KafkaContainer(
DockerImageName.parse("confluentinc/cp-kafka").withTag(CONFLUENT_PLATFORM_VERSION))
Expand Down
157 changes: 157 additions & 0 deletions api/src/test/java/io/kafbat/ui/ActiveDirectoryIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package io.kafbat.ui;

import static io.kafbat.ui.AbstractIntegrationTest.CONFLUENT_PLATFORM_VERSION;
import static io.kafbat.ui.AbstractIntegrationTest.LOCAL;
import static io.kafbat.ui.container.ActiveDirectoryContainer.DOMAIN;
import static io.kafbat.ui.container.ActiveDirectoryContainer.PASSWORD;
import static io.kafbat.ui.container.ActiveDirectoryContainer.SECOND_GROUP_USER;
import static io.kafbat.ui.container.ActiveDirectoryContainer.USER_WITHOUT_GROUP;
import static io.kafbat.ui.container.ActiveDirectoryContainer.FIRST_GROUP_USER;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import io.kafbat.ui.container.ActiveDirectoryContainer;
import java.util.List;
import java.util.Objects;
import io.kafbat.ui.model.AuthenticationInfoDTO;
import io.kafbat.ui.model.ResourceTypeDTO;
import io.kafbat.ui.model.TopicCreationDTO;
import io.kafbat.ui.model.TopicDTO;
import io.kafbat.ui.model.UserPermissionDTO;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters;
import org.testcontainers.containers.KafkaContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.utility.DockerImageName;

@SpringBootTest
@ActiveProfiles("rbac-ad")
@AutoConfigureWebTestClient(timeout = "60000")
@ContextConfiguration(initializers = {ActiveDirectoryIntegrationTest.Initializer.class})
public class ActiveDirectoryIntegrationTest {
private static final String SESSION = "SESSION";

private static final KafkaContainer KAFKA = new KafkaContainer(
DockerImageName.parse("confluentinc/cp-kafka").withTag(CONFLUENT_PLATFORM_VERSION))
.withNetwork(Network.SHARED);

private static final ActiveDirectoryContainer ACTIVE_DIRECTORY = new ActiveDirectoryContainer();

@Autowired
private WebTestClient webTestClient;

@BeforeAll
public static void setup() {
KAFKA.start();
ACTIVE_DIRECTORY.start();
}

@AfterAll
public static void shutdown() {
ACTIVE_DIRECTORY.stop();
KAFKA.stop();
}

@Test
public void testUserPermissions() {
AuthenticationInfoDTO info = authenticationInfo(FIRST_GROUP_USER);

assertNotNull(info);
assertTrue(info.getRbacEnabled());

List<UserPermissionDTO> permissions = info.getUserInfo().getPermissions();

assertFalse(permissions.isEmpty());
assertTrue(permissions.stream().anyMatch(permission ->
permission.getClusters().contains(LOCAL) &&
permission.getResource() == ResourceTypeDTO.TOPIC)
);
assertEquals(permissions, authenticationInfo(SECOND_GROUP_USER).getUserInfo().getPermissions());
}

@Test
public void testCreateTopic() {
TopicCreationDTO topic = new TopicCreationDTO()
.name("new")
.partitions(10);

TopicDTO result = webTestClient
.post()
.uri("/api/clusters/{clusterName}/topics", LOCAL)
.cookie(SESSION, session(FIRST_GROUP_USER))
.bodyValue(topic)
.exchange()
.expectStatus()
.isOk()
.returnResult(TopicDTO.class)
.getResponseBody()
.blockFirst();

assertNotNull(result);
assertEquals(topic.getName(), result.getName());
assertEquals(topic.getPartitions(), result.getPartitionCount());
}

@Test
public void testEmptyPermissions() {
assertTrue(Objects.requireNonNull(authenticationInfo(USER_WITHOUT_GROUP))
.getUserInfo()
.getPermissions()
.isEmpty()
);
}

private String session(String name) {
return Objects.requireNonNull(
webTestClient
.post()
.uri("/#")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData("username", name).with("password", PASSWORD))
.exchange()
.expectStatus()
.isFound()
.returnResult(String.class)
.getResponseCookies()
.getFirst("SESSION"))
.getValue();
}

private AuthenticationInfoDTO authenticationInfo(String name) {
return webTestClient
.get()
.uri("/api/authorization")
.cookie(SESSION, session(name))
.exchange()
.expectStatus()
.isOk()
.returnResult(AuthenticationInfoDTO.class)
.getResponseBody()
.blockFirst();
}

public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(@NotNull ConfigurableApplicationContext context) {
System.setProperty("kafka.clusters.0.name", LOCAL);
System.setProperty("kafka.clusters.0.bootstrapServers", KAFKA.getBootstrapServers());
System.setProperty("spring.ldap.urls", ACTIVE_DIRECTORY.getLdapUrl());
System.setProperty("oauth2.ldap.activeDirectory", "true");
System.setProperty("oauth2.ldap.activeDirectory.domain", DOMAIN);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.kafbat.ui.container;

import com.github.dockerjava.api.command.InspectContainerResponse;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;

@Slf4j
public class ActiveDirectoryContainer extends GenericContainer<ActiveDirectoryContainer> {
public static final DockerImageName IMAGE_NAME = DockerImageName.parse("nowsci/samba-domain:latest");
public static final String DOMAIN = "corp.kafbat.io";
public static final String PASSWORD = "StrongPassword123";
public static final String FIRST_GROUP_USER = "JohnDoe";
public static final String SECOND_GROUP_USER = "JohnWick";
public static final String USER_WITHOUT_GROUP = "JohnJames";

private static final String DOMAIN_DC = "dc=corp,dc=kafbat,dc=io";
private static final String GROUP = "group";
private static final String FIRST_GROUP = "firstGroup";
private static final String SECOND_GROUP = "secondGroup";
private static final String DOMAIN_EMAIL = "kafbat.io";
private static final String SAMBA_TOOL = "samba-tool";
private static final int LDAP_PORT = 389;

public ActiveDirectoryContainer() {
super(IMAGE_NAME);

withExposedPorts(LDAP_PORT);

withEnv("DOMAIN", DOMAIN);
withEnv("DOMAIN_DC", DOMAIN_DC);
withEnv("DOMAIN_EMAIL", DOMAIN_EMAIL);
withEnv("DOMAINPASS", PASSWORD);
withEnv("NOCOMPLEXITY", "true");
withEnv("INSECURELDAP", "true");

withPrivilegedMode(true);
}

protected void containerIsStarted(InspectContainerResponse containerInfo) {
createUser(USER_WITHOUT_GROUP);
createUser(FIRST_GROUP_USER);
createUser(SECOND_GROUP_USER);

exec(SAMBA_TOOL, GROUP, "add", FIRST_GROUP);
exec(SAMBA_TOOL, GROUP, "add", SECOND_GROUP);

exec(SAMBA_TOOL, GROUP, "addmembers", FIRST_GROUP, FIRST_GROUP_USER);
exec(SAMBA_TOOL, GROUP, "addmembers", SECOND_GROUP, SECOND_GROUP_USER);
}

public String getLdapUrl() {
return String.format("ldap://%s:%s", getHost(), getMappedPort(LDAP_PORT));
}

private void createUser(String name) {
exec(SAMBA_TOOL, "user", "create", name, PASSWORD, "--mail-address", name + '@' + DOMAIN_EMAIL);
exec(SAMBA_TOOL, "user", "setexpiry", name, "--noexpiry");
}

private void exec(String... cmd) {
ExecResult result;
try {
result = execInContainer(cmd);
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}

if (result.getStdout() != null && !result.getStdout().isEmpty()) {
log.info("Output: {}", result.getStdout());
}

if (result.getExitCode() != 0) {
throw new IllegalStateException(result.toString());
}
}
}
23 changes: 23 additions & 0 deletions api/src/test/resources/application-rbac-ad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
spring:
jmx:
enabled: true
auth:
type: LDAP
rbac:
roles:
- name: "roleName"
clusters:
- local
subjects:
- provider: ldap_ad
type: group
value: firstGroup
- provider: ldap_ad
type: group
value: secondGroup
permissions:
- resource: applicationconfig
actions: all
- resource: topic
value: ".*"
actions: all

0 comments on commit 4d3d510

Please # to comment.