Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(LDAP): add integration tests for LDAP Authorization #812

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Loading