Skip to content

Commit b155902

Browse files
committed
feat(LDAP): add integration tests for LDAP Authorization
closes #782
1 parent 5a40117 commit b155902

File tree

4 files changed

+255
-0
lines changed

4 files changed

+255
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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 org.junit.jupiter.api.AfterAll;
17+
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.Test;
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
21+
import org.springframework.boot.test.context.SpringBootTest;
22+
import org.springframework.context.ApplicationContextInitializer;
23+
import org.springframework.context.ConfigurableApplicationContext;
24+
import org.springframework.http.MediaType;
25+
import org.springframework.test.context.ActiveProfiles;
26+
import org.springframework.test.context.ContextConfiguration;
27+
import org.springframework.test.context.DynamicPropertyRegistry;
28+
import org.springframework.test.context.DynamicPropertySource;
29+
import org.springframework.test.web.reactive.server.WebTestClient;
30+
import org.springframework.web.reactive.function.BodyInserters;
31+
32+
@SpringBootTest
33+
@ActiveProfiles("rbac-ldap")
34+
@AutoConfigureWebTestClient(timeout = "60000")
35+
@ContextConfiguration(initializers = {OpenLdapPIntegrationTest.Initializer.class})
36+
class OpenLdapPIntegrationTest {
37+
private static final String SESSION = "SESSION";
38+
private static final OpenLdapContainer LDAP_CONTAINER = new OpenLdapContainer();
39+
40+
@Autowired
41+
private WebTestClient webTestClient;
42+
43+
@DynamicPropertySource
44+
static void neo4jProperties(DynamicPropertyRegistry registry) {
45+
registry.add("spring.ldap.urls", LDAP_CONTAINER::getLdapUrl);
46+
}
47+
48+
@BeforeAll
49+
static void setup() {
50+
LDAP_CONTAINER.start();
51+
}
52+
53+
@AfterAll
54+
static void shutdown() {
55+
LDAP_CONTAINER.stop();
56+
}
57+
58+
@Test
59+
public void testUserPermissions() {
60+
AuthenticationInfoDTO info = authenticationInfo("johndoe");
61+
62+
assertNotNull(info);
63+
assertTrue(info.getRbacEnabled());
64+
List<UserPermissionDTO> permissions = info.getUserInfo().getPermissions();
65+
assertFalse(permissions.isEmpty());
66+
assertTrue(permissions.stream().anyMatch(permission ->
67+
permission.getClusters().contains(LOCAL)
68+
&& permission.getResource() == ResourceTypeDTO.TOPIC
69+
&& permission.getActions().stream()
70+
.allMatch(action -> Action.fromValue(action.getValue()) != Action.ALL)
71+
)
72+
);
73+
assertEquals(permissions, authenticationInfo("johnwick").getUserInfo().getPermissions());
74+
assertEquals(permissions, authenticationInfo("jacksmith").getUserInfo().getPermissions());
75+
}
76+
77+
@Test
78+
public void testEmptyPermissions() {
79+
assertTrue(Objects.requireNonNull(authenticationInfo("johnjames"))
80+
.getUserInfo()
81+
.getPermissions()
82+
.isEmpty()
83+
);
84+
}
85+
86+
private String session(String name) {
87+
return Objects.requireNonNull(
88+
webTestClient
89+
.post()
90+
.uri("/login")
91+
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
92+
.body(BodyInserters.fromFormData("username", name).with("password", name + "@kafbat.io"))
93+
.exchange()
94+
.expectStatus()
95+
.isFound()
96+
.returnResult(String.class)
97+
.getResponseCookies()
98+
.getFirst(SESSION))
99+
.getValue();
100+
}
101+
102+
private AuthenticationInfoDTO authenticationInfo(String name) {
103+
return webTestClient
104+
.get()
105+
.uri("/api/authorization")
106+
.cookie(SESSION, session(name))
107+
.exchange()
108+
.expectStatus()
109+
.isOk()
110+
.returnResult(AuthenticationInfoDTO.class)
111+
.getResponseBody()
112+
.blockFirst();
113+
}
114+
115+
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
116+
117+
@Override
118+
public void initialize(ConfigurableApplicationContext context) {
119+
System.setProperty("spring.ldap.urls", LDAP_CONTAINER.getLdapUrl());
120+
}
121+
}
122+
}
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+
}
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)