Skip to content

Commit

Permalink
feat(LDAP): add integration tests for LDAP Authorization
Browse files Browse the repository at this point in the history
closes #782
  • Loading branch information
sixdouglas committed Feb 3, 2025
1 parent 5a40117 commit b155902
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 0 deletions.
122 changes: 122 additions & 0 deletions api/src/test/java/io/kafbat/ui/OpenLdapPIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package io.kafbat.ui;

import static io.kafbat.ui.AbstractIntegrationTest.LOCAL;
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.api.model.Action;
import io.kafbat.ui.container.OpenLdapContainer;
import io.kafbat.ui.model.AuthenticationInfoDTO;
import io.kafbat.ui.model.ResourceTypeDTO;
import io.kafbat.ui.model.UserPermissionDTO;
import java.util.List;
import java.util.Objects;
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.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters;

@SpringBootTest
@ActiveProfiles("rbac-ldap")
@AutoConfigureWebTestClient(timeout = "60000")
@ContextConfiguration(initializers = {OpenLdapPIntegrationTest.Initializer.class})
class OpenLdapPIntegrationTest {
private static final String SESSION = "SESSION";
private static final OpenLdapContainer LDAP_CONTAINER = new OpenLdapContainer();

@Autowired
private WebTestClient webTestClient;

@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.ldap.urls", LDAP_CONTAINER::getLdapUrl);
}

@BeforeAll
static void setup() {
LDAP_CONTAINER.start();
}

@AfterAll
static void shutdown() {
LDAP_CONTAINER.stop();
}

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

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
&& permission.getActions().stream()
.allMatch(action -> Action.fromValue(action.getValue()) != Action.ALL)
)
);
assertEquals(permissions, authenticationInfo("johnwick").getUserInfo().getPermissions());
assertEquals(permissions, authenticationInfo("jacksmith").getUserInfo().getPermissions());
}

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

private String session(String name) {
return Objects.requireNonNull(
webTestClient
.post()
.uri("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData("username", name).with("password", name + "@kafbat.io"))
.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(ConfigurableApplicationContext context) {
System.setProperty("spring.ldap.urls", LDAP_CONTAINER.getLdapUrl());
}
}
}
34 changes: 34 additions & 0 deletions api/src/test/java/io/kafbat/ui/container/OpenLdapContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.kafbat.ui.container;

import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.MountableFile;

@Slf4j
public class OpenLdapContainer extends GenericContainer<OpenLdapContainer> {
public static final String ADMIN_PASSWORD = "StrongPassword123";
private static final String DOMAIN = "kafbat.io";
private static final String DOMAIN_DC = "dc=kafbat,dc=io";
private static final int LDAP_PORT = 1389;
private static final DockerImageName IMAGE_NAME = DockerImageName.parse("bitnami/openldap:2.6.9");

public OpenLdapContainer() {
super(IMAGE_NAME);

withExposedPorts(LDAP_PORT);

withEnv("LDAP_ORGANISATION", DOMAIN.replace(".", ""));
withEnv("LDAP_DOMAIN", DOMAIN);
withEnv("LDAP_ROOT", DOMAIN_DC);
withEnv("LDAP_ADMIN_DN", "cn=admin," + DOMAIN_DC);
withEnv("LDAP_ADMIN_PASSWORD", ADMIN_PASSWORD);
withEnv("LDAP_LOGLEVEL", "-1");

withCopyFileToContainer(MountableFile.forClasspathResource("/open-ldap/"), "/ldifs/");
}

public String getLdapUrl() {
return String.format("ldap://%s:%s", getHost(), getMappedPort(LDAP_PORT));
}
}
35 changes: 35 additions & 0 deletions api/src/test/resources/application-rbac-ldap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
spring:
ldap:
base: "cn={0},ou=people,dc=kafbat,dc=io"
admin-user: "cn=admin,dc=kafbat,dc=io"
admin-password: "StrongPassword123"
user-filter-search-base: "dc=kafbat,dc=io"
user-filter-search-filter: "(&(uid={0})(objectClass=inetOrgPerson))"
group-filter-search-base: "ou=people,dc=kafbat,dc=io" # required for RBAC
logging:
level:
root: info

auth:
type: LDAP
rbac:
roles:
- name: "roleName"
clusters:
- local
subjects:
- provider: ldap
type: group
value: firstGroup
- provider: ldap
type: group
value: secondGroup
- provider: ldap
type: user
value: jacksmith
permissions:
- resource: applicationconfig
actions: all
- resource: topic
value: ".*"
actions: all
64 changes: 64 additions & 0 deletions api/src/test/resources/open-ldap/export.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
dn: dc=kafbat,dc=io
objectClass: dcObject
objectClass: organization
dc: kafbat
o: kafbat

# dn: ou=groups,dc=kafbat,dc=io
# ou: groups
# objectClass: organizationalUnit

dn: ou=people,dc=kafbat,dc=io
ou: people
objectClass: top
objectClass: organizationalUnit

dn: cn=johndoe,ou=people,dc=kafbat,dc=io
sn: JohnDoe
cn: johndoe
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
userPassword: [email protected]

dn: cn=johnwick,ou=people,dc=kafbat,dc=io
sn: JohnWick
cn: johnwick
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
userPassword: [email protected]

dn: cn=jacksmith,ou=people,dc=kafbat,dc=io
sn: JackSmith
cn: jacksmith
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
userPassword: [email protected]

dn: cn=johnjames,ou=people,dc=kafbat,dc=io
sn: JohnJames
cn: johnjames
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
userPassword: [email protected]

dn: cn=firstGroup,ou=people,dc=kafbat,dc=io
description: App First Group Team
cn: firstGroup
objectClass: top
objectClass: groupOfNames
member: cn=johndoe,ou=people,dc=kafbat,dc=io

dn: cn=secondGroup,ou=people,dc=kafbat,dc=io
cn: secondGroup
objectClass: top
objectClass: groupOfNames
member: cn=johnwick,ou=people,dc=kafbat,dc=io

0 comments on commit b155902

Please sign in to comment.