Skip to content

Commit 719e0fc

Browse files
committed
feat(LDAP): add integration tests for LDAP Authorization
closes #782
1 parent 49894b8 commit 719e0fc

File tree

5 files changed

+262
-0
lines changed

5 files changed

+262
-0
lines changed

api/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@
139139
<groupId>org.springframework.boot</groupId>
140140
<artifactId>spring-boot-starter-test</artifactId>
141141
<scope>test</scope>
142+
<exclusions>
143+
<exclusion>
144+
<groupId>com.vaadin.external.google</groupId>
145+
<artifactId>android-json</artifactId>
146+
</exclusion>
147+
</exclusions>
142148
</dependency>
143149
<dependency>
144150
<groupId>io.projectreactor</groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package io.kafbat.ui;
2+
3+
import static io.kafbat.ui.AbstractIntegrationTest.LOCAL;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertFalse;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
import io.kafbat.ui.api.model.Action;
10+
import io.kafbat.ui.container.OpenLdapContainer;
11+
import io.kafbat.ui.model.AuthenticationInfoDTO;
12+
import io.kafbat.ui.model.ResourceTypeDTO;
13+
import io.kafbat.ui.model.UserPermissionDTO;
14+
import java.util.List;
15+
import java.util.Objects;
16+
import lombok.extern.slf4j.Slf4j;
17+
import org.junit.jupiter.api.AfterAll;
18+
import org.junit.jupiter.api.BeforeAll;
19+
import org.junit.jupiter.api.Test;
20+
import org.springframework.beans.factory.annotation.Autowired;
21+
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
22+
import org.springframework.boot.test.context.SpringBootTest;
23+
import org.springframework.context.ApplicationContextInitializer;
24+
import org.springframework.context.ConfigurableApplicationContext;
25+
import org.springframework.http.MediaType;
26+
import org.springframework.test.context.ActiveProfiles;
27+
import org.springframework.test.context.ContextConfiguration;
28+
import org.springframework.test.web.reactive.server.WebTestClient;
29+
import org.springframework.web.reactive.function.BodyInserters;
30+
import org.testcontainers.containers.output.Slf4jLogConsumer;
31+
32+
@Slf4j
33+
@SpringBootTest
34+
@ActiveProfiles("rbac-ldap")
35+
@AutoConfigureWebTestClient(timeout = "60000")
36+
@ContextConfiguration(initializers = {OpenLdapIntegrationTest.Initializer.class})
37+
class OpenLdapIntegrationTest {
38+
private static final String SESSION = "SESSION";
39+
private static final OpenLdapContainer LDAP_CONTAINER = new OpenLdapContainer();
40+
41+
@Autowired
42+
private WebTestClient webTestClient;
43+
44+
@BeforeAll
45+
static void setup() {
46+
LDAP_CONTAINER.start();
47+
Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log);
48+
LDAP_CONTAINER.followOutput(logConsumer);
49+
}
50+
51+
@AfterAll
52+
static void shutdown() {
53+
LDAP_CONTAINER.stop();
54+
}
55+
56+
@Test
57+
public void testUserPermissions() {
58+
AuthenticationInfoDTO info = authenticationInfo("johndoe");
59+
60+
assertNotNull(info);
61+
assertTrue(info.getRbacEnabled());
62+
List<UserPermissionDTO> permissions = info.getUserInfo().getPermissions();
63+
assertFalse(permissions.isEmpty());
64+
assertTrue(permissions.stream().anyMatch(permission ->
65+
permission.getClusters().contains(LOCAL)
66+
&& permission.getResource() == ResourceTypeDTO.TOPIC
67+
&& permission.getActions().stream()
68+
.allMatch(action -> Action.fromValue(action.getValue()) != Action.ALL)
69+
)
70+
);
71+
assertEquals(permissions, authenticationInfo("johnwick").getUserInfo().getPermissions());
72+
assertEquals(permissions, authenticationInfo("jacksmith").getUserInfo().getPermissions());
73+
}
74+
75+
@Test
76+
public void testEmptyPermissions() {
77+
assertTrue(Objects.requireNonNull(authenticationInfo("johnjames"))
78+
.getUserInfo()
79+
.getPermissions()
80+
.isEmpty()
81+
);
82+
}
83+
84+
private String session(String name) {
85+
return Objects.requireNonNull(
86+
webTestClient
87+
.post()
88+
.uri("/login")
89+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
90+
.body(BodyInserters.fromFormData("username", name).with("password", name + "@kafbat.io"))
91+
.exchange()
92+
.expectStatus()
93+
.isFound()
94+
.returnResult(String.class)
95+
.getResponseCookies()
96+
.getFirst(SESSION))
97+
.getValue();
98+
}
99+
100+
private AuthenticationInfoDTO authenticationInfo(String name) {
101+
return webTestClient
102+
.get()
103+
.uri("/api/authorization")
104+
.cookie(SESSION, session(name))
105+
.exchange()
106+
.expectStatus()
107+
.isOk()
108+
.returnResult(AuthenticationInfoDTO.class)
109+
.getResponseBody()
110+
.blockFirst();
111+
}
112+
113+
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
114+
115+
@Override
116+
public void initialize(ConfigurableApplicationContext context) {
117+
System.setProperty("spring.ldap.urls", LDAP_CONTAINER.getLdapUrl());
118+
System.setProperty("oauth2.ldap.activeDirectory", "false");
119+
}
120+
}
121+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.kafbat.ui.container;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.testcontainers.containers.GenericContainer;
5+
import org.testcontainers.containers.wait.strategy.Wait;
6+
import org.testcontainers.utility.DockerImageName;
7+
import org.testcontainers.utility.MountableFile;
8+
9+
@Slf4j
10+
public class OpenLdapContainer extends GenericContainer<OpenLdapContainer> {
11+
public static final String ADMIN_PASSWORD = "StrongPassword123";
12+
public static final String DOMAIN = "kafbat.io";
13+
private static final String DOMAIN_DC = "dc=kafbat,dc=io";
14+
private static final int LDAP_PORT = 1389;
15+
private static final DockerImageName IMAGE_NAME = DockerImageName.parse("bitnami/openldap:2.6.9");
16+
17+
public OpenLdapContainer() {
18+
super(IMAGE_NAME);
19+
20+
withExposedPorts(LDAP_PORT);
21+
22+
withEnv("LDAP_ORGANISATION", DOMAIN.replace(".", ""));
23+
withEnv("LDAP_DOMAIN", DOMAIN);
24+
withEnv("LDAP_ROOT", DOMAIN_DC);
25+
withEnv("LDAP_ADMIN_DN", "cn=admin," + DOMAIN_DC);
26+
withEnv("LDAP_ADMIN_PASSWORD", ADMIN_PASSWORD);
27+
withEnv("LDAP_LOGLEVEL", "512");
28+
29+
withCopyFileToContainer(MountableFile.forClasspathResource("/open-ldap/"), "/ldifs/");
30+
waitingFor(Wait.forLogMessage(".*slapd starting.*", 1));
31+
}
32+
33+
public String getLdapUrl() {
34+
return String.format("ldap://%s:%s", getHost(), getMappedPort(LDAP_PORT));
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
logging:
10+
level:
11+
root: info
12+
13+
auth:
14+
type: LDAP
15+
rbac:
16+
roles:
17+
- name: "roleName"
18+
clusters:
19+
- local
20+
subjects:
21+
- provider: ldap
22+
type: group
23+
value: firstGroup
24+
- provider: ldap
25+
type: group
26+
value: secondGroup
27+
- provider: ldap
28+
type: user
29+
value: jacksmith
30+
permissions:
31+
- resource: applicationconfig
32+
actions: all
33+
- resource: topic
34+
value: ".*"
35+
actions: all
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)