fake-CAS integration for tests

This commit is contained in:
Michael Hoennig 2024-12-18 17:14:48 +01:00
parent 87c7d2f531
commit 2ec44f1a43
7 changed files with 133 additions and 3 deletions

View File

@ -0,0 +1,35 @@
package net.hostsharing.hsadminng.config;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CasAuthenticationFilter implements Filter {
@Autowired
private CasServiceTicketValidator ticketValidator;
@Override
@SneakyThrows
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) {
final var httpRequest = (HttpServletRequest) request;
final var httpResponse = (HttpServletResponse) response;
final var ticket = httpRequest.getHeader("Authorization");
if (ticket == null || !ticketValidator.validateTicket(ticket)) {
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
chain.doFilter(request, response);
}
}

View File

@ -0,0 +1,40 @@
package net.hostsharing.hsadminng.config;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.xml.parsers.DocumentBuilderFactory;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@Service
public class CasServiceTicketValidator {
@Value("${hsadminng.cas.server-url}")
private String casServerUrl;
@Value("${hsadminng.cas.service-url}")
private String serviceUrl;
private final RestTemplate restTemplate = new RestTemplate();
@SneakyThrows
public boolean validateTicket(final String ticket) {
if (casServerUrl.equals("fake") && ticket.equals("test")) {
return true;
}
final var url = casServerUrl + "/p3/serviceValidate" +
"?service=" + URLEncoder.encode(serviceUrl, StandardCharsets.UTF_8) +
"&ticket=" + URLEncoder.encode(ticket, StandardCharsets.UTF_8);
final var response = restTemplate.getForObject(url, String.class);
final var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new java.io.ByteArrayInputStream(response.getBytes()));
return doc.getElementsByTagName("cas:authenticationSuccess").getLength() > 0;
}
}

View File

@ -36,6 +36,9 @@ liquibase:
hsadminng: hsadminng:
postgres: postgres:
leakproof: leakproof:
cas:
server-url: https://cas.example.com/cas
service-url: http://localhost:8080/api # TODO.conf: deployment target
metrics: metrics:
distribution: distribution:

View File

@ -0,0 +1,31 @@
package net.hostsharing.hsadminng.config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"server.port=0"})
// IMPORTANT: To test prod config, do not use test profile!
class CasAuthenticationFilterIntegrationTest {
@Value("${local.server.port}")
private int serverPort;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void shouldRejectRequest() {
final var result = this.restTemplate.getForEntity(
"http://localhost:" + this.serverPort + "/api/ping", String.class);
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
}

View File

@ -8,13 +8,16 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"management.port=0", "server.port=0"}) @TestPropertySource(properties = {"management.port=0", "server.port=0", "hsadminng.cas.server-url=fake"})
// IMPORTANT: To test prod config, do not use test profile! // IMPORTANT: To test prod config, do not use test profile!
class WebSecurityConfigIntegrationTest { class WebSecurityConfigIntegrationTest {
@ -29,8 +32,18 @@ class WebSecurityConfigIntegrationTest {
@Test @Test
public void shouldSupportPingEndpoint() { public void shouldSupportPingEndpoint() {
final var result = this.restTemplate.getForEntity( // fake Authorization header
"http://localhost:" + this.serverPort + "/api/ping", String.class); final var headers = new HttpHeaders();
headers.set("Authorization", "test");
// http request
final var result = restTemplate.exchange(
"http://localhost:" + this.serverPort + "/api/ping",
HttpMethod.GET,
new HttpEntity<Object>(null, headers),
String.class
);
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(result.getBody()).startsWith("pong"); assertThat(result.getBody()).startsWith("pong");
} }

View File

@ -160,6 +160,7 @@ public abstract class UseCase<T extends UseCase<?>> {
.GET() .GET()
.uri(new URI("http://localhost:" + testSuite.port + uriPath)) .uri(new URI("http://localhost:" + testSuite.port + uriPath))
.header("current-subject", ScenarioTest.RUN_AS_USER) .header("current-subject", ScenarioTest.RUN_AS_USER)
.header("Authorization", "test")
.timeout(seconds(10)) .timeout(seconds(10))
.build(); .build();
final var response = client.send(request, BodyHandlers.ofString()); final var response = client.send(request, BodyHandlers.ofString());
@ -175,6 +176,7 @@ public abstract class UseCase<T extends UseCase<?>> {
.uri(new URI("http://localhost:" + testSuite.port + uriPath)) .uri(new URI("http://localhost:" + testSuite.port + uriPath))
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("current-subject", ScenarioTest.RUN_AS_USER) .header("current-subject", ScenarioTest.RUN_AS_USER)
.header("Authorization", "test")
.timeout(seconds(10)) .timeout(seconds(10))
.build(); .build();
final var response = client.send(request, BodyHandlers.ofString()); final var response = client.send(request, BodyHandlers.ofString());
@ -190,6 +192,7 @@ public abstract class UseCase<T extends UseCase<?>> {
.uri(new URI("http://localhost:" + testSuite.port + uriPath)) .uri(new URI("http://localhost:" + testSuite.port + uriPath))
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("current-subject", ScenarioTest.RUN_AS_USER) .header("current-subject", ScenarioTest.RUN_AS_USER)
.header("Authorization", "test")
.timeout(seconds(10)) .timeout(seconds(10))
.build(); .build();
final var response = client.send(request, BodyHandlers.ofString()); final var response = client.send(request, BodyHandlers.ofString());
@ -204,6 +207,7 @@ public abstract class UseCase<T extends UseCase<?>> {
.uri(new URI("http://localhost:" + testSuite.port + uriPath)) .uri(new URI("http://localhost:" + testSuite.port + uriPath))
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("current-subject", ScenarioTest.RUN_AS_USER) .header("current-subject", ScenarioTest.RUN_AS_USER)
.header("Authorization", "test")
.timeout(seconds(10)) .timeout(seconds(10))
.build(); .build();
final var response = client.send(request, BodyHandlers.ofString()); final var response = client.send(request, BodyHandlers.ofString());

View File

@ -51,3 +51,7 @@ testcontainers:
network: network:
mode: host mode: host
hsadminng:
cas:
server-url: fake
service-url: http://localhost:8080/api # not really used in test config