Skip to content

Commit 26291e9

Browse files
committed
feat(LDAP): add integration tests for LDAP Authorization
closes kafbat#782
1 parent ff17c7a commit 26291e9

File tree

4 files changed

+265
-0
lines changed

4 files changed

+265
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package io.kafbat.ui;
2+
3+
import io.kafbat.ui.container.OpenLdapContainer;
4+
import io.kafbat.ui.model.AuthenticationInfoDTO;
5+
import io.kafbat.ui.model.ResourceTypeDTO;
6+
import io.kafbat.ui.model.UserPermissionDTO;
7+
import org.junit.jupiter.api.AfterAll;
8+
import org.junit.jupiter.api.BeforeAll;
9+
import org.junit.jupiter.api.Test;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
12+
import org.springframework.boot.test.context.SpringBootTest;
13+
import org.springframework.context.ApplicationContextInitializer;
14+
import org.springframework.context.ConfigurableApplicationContext;
15+
import org.springframework.http.MediaType;
16+
import org.springframework.test.context.ActiveProfiles;
17+
import org.springframework.test.context.ContextConfiguration;
18+
import org.springframework.test.context.DynamicPropertyRegistry;
19+
import org.springframework.test.context.DynamicPropertySource;
20+
import org.springframework.test.web.reactive.server.WebTestClient;
21+
import org.springframework.web.reactive.function.BodyInserters;
22+
import java.util.List;
23+
import java.util.Objects;
24+
25+
import static io.kafbat.ui.AbstractIntegrationTest.LOCAL;
26+
import static org.junit.jupiter.api.Assertions.assertEquals;
27+
import static org.junit.jupiter.api.Assertions.assertFalse;
28+
import static org.junit.jupiter.api.Assertions.assertNotNull;
29+
import static org.junit.jupiter.api.Assertions.assertTrue;
30+
31+
@SpringBootTest
32+
@ActiveProfiles("rbac-ldap")
33+
@AutoConfigureWebTestClient(timeout = "60000")
34+
@ContextConfiguration(initializers = {OpenLDAPIntegrationTest.Initializer.class})
35+
class OpenLDAPIntegrationTest {
36+
private static final String SESSION = "SESSION";
37+
private static final OpenLdapContainer LDAP_CONTAINER = new OpenLdapContainer();
38+
39+
@Autowired
40+
private WebTestClient webTestClient;
41+
42+
@DynamicPropertySource
43+
static void neo4jProperties(DynamicPropertyRegistry registry) {
44+
registry.add("spring.ldap.urls", LDAP_CONTAINER::getLdapUrl);
45+
}
46+
47+
@BeforeAll
48+
static void setup() {
49+
LDAP_CONTAINER.start();
50+
}
51+
52+
@AfterAll
53+
static void shutdown() {
54+
LDAP_CONTAINER.stop();
55+
}
56+
57+
@Test
58+
public void testUserPermissions() {
59+
AuthenticationInfoDTO info = authenticationInfo("johndoe");
60+
61+
assertNotNull(info);
62+
assertTrue(info.getRbacEnabled());
63+
System.out.println("info = " + info);
64+
List<UserPermissionDTO> permissions = info.getUserInfo().getPermissions();
65+
assertFalse(permissions.isEmpty());
66+
assertTrue(permissions.stream().anyMatch(permission ->
67+
permission.getClusters().contains(LOCAL) && permission.getResource() == ResourceTypeDTO.TOPIC));
68+
assertEquals(permissions, authenticationInfo("johnwick").getUserInfo().getPermissions());
69+
assertEquals(permissions, authenticationInfo("jacksmith").getUserInfo().getPermissions());
70+
}
71+
72+
@Test
73+
public void testDirectUserPermissions() {
74+
AuthenticationInfoDTO info = authenticationInfo("jacksmith");
75+
76+
assertNotNull(info);
77+
assertTrue(info.getRbacEnabled());
78+
System.out.println("info = " + info);
79+
List<UserPermissionDTO> permissions = info.getUserInfo().getPermissions();
80+
assertFalse(permissions.isEmpty());
81+
}
82+
83+
@Test
84+
public void testEmptyPermissions() {
85+
assertTrue(Objects.requireNonNull(authenticationInfo("johnjames"))
86+
.getUserInfo()
87+
.getPermissions()
88+
.isEmpty()
89+
);
90+
}
91+
92+
private String session(String name) {
93+
return Objects.requireNonNull(
94+
webTestClient
95+
.post()
96+
.uri("/login")
97+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
98+
.body(BodyInserters.fromFormData("username", name).with("password", name + "@kafbat.io"))
99+
.exchange()
100+
.expectStatus()
101+
.isFound()
102+
.returnResult(String.class)
103+
.getResponseCookies()
104+
.getFirst(SESSION))
105+
.getValue();
106+
}
107+
108+
private AuthenticationInfoDTO authenticationInfo(String name) {
109+
return webTestClient
110+
.get()
111+
.uri("/api/authorization")
112+
.cookie(SESSION, session(name))
113+
.exchange()
114+
.expectStatus()
115+
.isOk()
116+
.returnResult(AuthenticationInfoDTO.class)
117+
.getResponseBody()
118+
.blockFirst();
119+
}
120+
121+
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
122+
123+
@Override
124+
public void initialize(ConfigurableApplicationContext context) {
125+
System.setProperty("spring.ldap.urls", LDAP_CONTAINER.getLdapUrl());
126+
System.setProperty("oauth2.ldap.activeDirectory", "false");
127+
}
128+
}
129+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.kafbat.ui.container;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.testcontainers.containers.GenericContainer;
5+
import org.testcontainers.utility.DockerImageName;
6+
import org.testcontainers.utility.MountableFile;
7+
8+
@Slf4j
9+
public class OpenLdapContainer extends GenericContainer<OpenLdapContainer> {
10+
public static final String ADMIN_PASSWORD = "StrongPassword123";
11+
private static final String DOMAIN = "kafbat.io";
12+
private static final String DOMAIN_DC = "dc=kafbat,dc=io";
13+
private static final int LDAP_PORT = 1389;
14+
private static final DockerImageName IMAGE_NAME = DockerImageName.parse("bitnami/openldap:2.6.9");
15+
16+
public OpenLdapContainer() {
17+
super(IMAGE_NAME);
18+
19+
withExposedPorts(LDAP_PORT);
20+
21+
withEnv("LDAP_ORGANISATION", DOMAIN.replace(".", ""));
22+
withEnv("LDAP_DOMAIN", DOMAIN);
23+
withEnv("LDAP_ROOT", DOMAIN_DC);
24+
withEnv("LDAP_ADMIN_DN", "cn=admin," + DOMAIN_DC);
25+
withEnv("LDAP_ADMIN_PASSWORD", ADMIN_PASSWORD);
26+
withEnv("LDAP_LOGLEVEL", "-1");
27+
28+
withCopyFileToContainer(MountableFile.forClasspathResource("/open-ldap/"), "/ldifs/");
29+
}
30+
31+
public String getLdapUrl() {
32+
return String.format("ldap://%s:%s", getHost(), getMappedPort(LDAP_PORT));
33+
}
34+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
spring:
2+
ldap:
3+
base: "cn={0},ou=people,dc=kafbat,dc=io"
4+
admin-user: "cn=admin,dc=kafbat,dc=io"
5+
admin-password: "StrongPassword123"
6+
user-filter-search-base: "dc=kafbat,dc=io"
7+
user-filter-search-filter: "(&(uid={0})(objectClass=inetOrgPerson))"
8+
group-filter-search-base: "ou=people,dc=kafbat,dc=io" # required for RBAC
9+
oauth2:
10+
ldap:
11+
activeDirectory: false
12+
logging:
13+
level:
14+
root: info
15+
16+
auth:
17+
type: LDAP
18+
rbac:
19+
roles:
20+
- name: "roleName"
21+
clusters:
22+
- local
23+
subjects:
24+
- provider: ldap
25+
type: group
26+
value: firstGroup
27+
- provider: ldap
28+
type: group
29+
value: secondGroup
30+
- provider: ldap
31+
type: user
32+
value: jacksmith
33+
permissions:
34+
- resource: applicationconfig
35+
actions: all
36+
- resource: topic
37+
value: ".*"
38+
actions: all
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
dn: dc=kafbat,dc=io
2+
objectClass: dcObject
3+
objectClass: organization
4+
dc: kafbat
5+
o: kafbat
6+
7+
# dn: ou=groups,dc=kafbat,dc=io
8+
# ou: groups
9+
# objectClass: organizationalUnit
10+
11+
dn: ou=people,dc=kafbat,dc=io
12+
ou: people
13+
objectClass: top
14+
objectClass: organizationalUnit
15+
16+
dn: cn=johndoe,ou=people,dc=kafbat,dc=io
17+
sn: JohnDoe
18+
cn: johndoe
19+
objectClass: top
20+
objectClass: person
21+
objectClass: organizationalPerson
22+
objectClass: inetOrgPerson
23+
userPassword: [email protected]
24+
25+
dn: cn=johnwick,ou=people,dc=kafbat,dc=io
26+
sn: JohnWick
27+
cn: johnwick
28+
objectClass: top
29+
objectClass: person
30+
objectClass: organizationalPerson
31+
objectClass: inetOrgPerson
32+
userPassword: [email protected]
33+
34+
dn: cn=jacksmith,ou=people,dc=kafbat,dc=io
35+
sn: JackSmith
36+
cn: jacksmith
37+
objectClass: top
38+
objectClass: person
39+
objectClass: organizationalPerson
40+
objectClass: inetOrgPerson
41+
userPassword: [email protected]
42+
43+
dn: cn=johnjames,ou=people,dc=kafbat,dc=io
44+
sn: JohnJames
45+
cn: johnjames
46+
objectClass: top
47+
objectClass: person
48+
objectClass: organizationalPerson
49+
objectClass: inetOrgPerson
50+
userPassword: [email protected]
51+
52+
dn: cn=firstGroup,ou=people,dc=kafbat,dc=io
53+
description: App First Group Team
54+
cn: firstGroup
55+
objectClass: top
56+
objectClass: groupOfNames
57+
member: cn=johndoe,ou=people,dc=kafbat,dc=io
58+
59+
dn: cn=secondGroup,ou=people,dc=kafbat,dc=io
60+
cn: secondGroup
61+
objectClass: top
62+
objectClass: groupOfNames
63+
member: cn=johnwick,ou=people,dc=kafbat,dc=io
64+

0 commit comments

Comments
 (0)