From 2ec44f1a43797e2978b01db0c3f268f6146c16c3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 18 Dec 2024 17:14:48 +0100 Subject: [PATCH 01/24] fake-CAS integration for tests --- .../config/CasAuthenticationFilter.java | 35 ++++++++++++++++ .../config/CasServiceTicketValidator.java | 40 +++++++++++++++++++ src/main/resources/application.yml | 3 ++ ...asAuthenticationFilterIntegrationTest.java | 31 ++++++++++++++ .../WebSecurityConfigIntegrationTest.java | 19 +++++++-- .../hsadminng/hs/scenarios/UseCase.java | 4 ++ src/test/resources/application.yml | 4 ++ 7 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java create mode 100644 src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java new file mode 100644 index 00000000..d81c13a8 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java @@ -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); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java b/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java new file mode 100644 index 00000000..bb1c78cb --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java @@ -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; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f75ae429..054973b2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,6 +36,9 @@ liquibase: hsadminng: postgres: leakproof: + cas: + server-url: https://cas.example.com/cas + service-url: http://localhost:8080/api # TODO.conf: deployment target metrics: distribution: diff --git a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java new file mode 100644 index 00000000..14940868 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java @@ -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); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java index a69ca9f4..de8c63ea 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java @@ -8,13 +8,16 @@ 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.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; 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 = {"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! class WebSecurityConfigIntegrationTest { @@ -29,8 +32,18 @@ class WebSecurityConfigIntegrationTest { @Test public void shouldSupportPingEndpoint() { - final var result = this.restTemplate.getForEntity( - "http://localhost:" + this.serverPort + "/api/ping", String.class); + // fake Authorization header + 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(null, headers), + String.class + ); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(result.getBody()).startsWith("pong"); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java index 01c5dede..d0167933 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java @@ -160,6 +160,7 @@ public abstract class UseCase> { .GET() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("current-subject", ScenarioTest.RUN_AS_USER) + .header("Authorization", "test") .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -175,6 +176,7 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) + .header("Authorization", "test") .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -190,6 +192,7 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) + .header("Authorization", "test") .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -204,6 +207,7 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) + .header("Authorization", "test") .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index f0df4e4b..35ec34bf 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -51,3 +51,7 @@ testcontainers: network: mode: host +hsadminng: + cas: + server-url: fake + service-url: http://localhost:8080/api # not really used in test config -- 2.39.5 From 2338c3cc57c4fb89cf2f369820833b9803da0dfc Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 18 Dec 2024 17:20:33 +0100 Subject: [PATCH 02/24] do not require CAS service ticket for tests --- .../hostsharing/hsadminng/config/CasAuthenticationFilter.java | 2 +- .../hostsharing/hsadminng/config/CasServiceTicketValidator.java | 2 +- .../java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java index d81c13a8..9f7be5bc 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java @@ -25,7 +25,7 @@ public class CasAuthenticationFilter implements Filter { final var ticket = httpRequest.getHeader("Authorization"); - if (ticket == null || !ticketValidator.validateTicket(ticket)) { + if (!ticketValidator.validateTicket(ticket)) { httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java b/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java index bb1c78cb..18f5a52a 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java @@ -22,7 +22,7 @@ public class CasServiceTicketValidator { @SneakyThrows public boolean validateTicket(final String ticket) { - if (casServerUrl.equals("fake") && ticket.equals("test")) { + if (casServerUrl.equals("fake")) { return true; } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java index d0167933..93943e73 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java @@ -207,7 +207,6 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); -- 2.39.5 From 695341c3dcbbb5bc1b998418ce74885e4fb616f2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 18 Dec 2024 17:22:00 +0100 Subject: [PATCH 03/24] add FIXMEs --- .../net/hostsharing/hsadminng/hs/scenarios/UseCase.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java index 93943e73..fc8000c7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java @@ -160,7 +160,7 @@ public abstract class UseCase> { .GET() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") + .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -176,7 +176,7 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") + .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -192,7 +192,7 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") + .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -207,6 +207,7 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) + .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); -- 2.39.5 From ee001b520ceb022f6c62bf07a08052af5bc7aee0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 11:18:35 +0100 Subject: [PATCH 04/24] fix most tests with improved fake CAS-validator and use WireMock --- ...uthenticatedHttpServletRequestWrapper.java | 54 +++++++++++++++ .../config/CasAuthenticationFilter.java | 16 +++-- .../hsadminng/config/CasAuthenticator.java | 54 +++++++++++++++ .../config/CasServiceTicketValidator.java | 40 ------------ .../hsadminng/ping/PingController.java | 10 ++- ...asAuthenticationFilterIntegrationTest.java | 65 +++++++++++++++++-- .../WebSecurityConfigIntegrationTest.java | 2 +- .../hsadminng/test/DisableSecurityConfig.java | 6 ++ .../hsadminng/test/WireMockConfig.java | 19 ++++++ 9 files changed, 213 insertions(+), 53 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/config/AuthenticatedHttpServletRequestWrapper.java create mode 100644 src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java delete mode 100644 src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java diff --git a/src/main/java/net/hostsharing/hsadminng/config/AuthenticatedHttpServletRequestWrapper.java b/src/main/java/net/hostsharing/hsadminng/config/AuthenticatedHttpServletRequestWrapper.java new file mode 100644 index 00000000..8f69136a --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/config/AuthenticatedHttpServletRequestWrapper.java @@ -0,0 +1,54 @@ +package net.hostsharing.hsadminng.config; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import java.util.*; + +public class AuthenticatedHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private final Map customHeaders = new HashMap<>(); + + public AuthenticatedHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + } + + public void addHeader(final String name, final String value) { + customHeaders.put(name, value); + } + + @Override + public String getHeader(final String name) { + // Check custom headers first + final var customHeaderValue = customHeaders.get(name); + if (customHeaderValue != null) { + return customHeaderValue; + } + // Fall back to the original headers + return super.getHeader(name); + } + + @Override + public Enumeration getHeaderNames() { + // Combine original headers and custom headers + final var headerNames = new HashSet<>(customHeaders.keySet()); + final var originalHeaderNames = super.getHeaderNames(); + while (originalHeaderNames.hasMoreElements()) { + headerNames.add(originalHeaderNames.nextElement()); + } + return Collections.enumeration(headerNames); + } + + @Override + public Enumeration getHeaders(final String name) { + // Combine original headers and custom header + final var values = new HashSet(); + if (customHeaders.containsKey(name)) { + values.add(customHeaders.get(name)); + } + final var originalValues = super.getHeaders(name); + while (originalValues.hasMoreElements()) { + values.add(originalValues.nextElement()); + } + return Collections.enumeration(values); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java index 9f7be5bc..404a4027 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java @@ -9,13 +9,14 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.stereotype.Component; @Component public class CasAuthenticationFilter implements Filter { @Autowired - private CasServiceTicketValidator ticketValidator; + private CasAuthenticator casAuthenticator; @Override @SneakyThrows @@ -23,13 +24,16 @@ public class CasAuthenticationFilter implements Filter { final var httpRequest = (HttpServletRequest) request; final var httpResponse = (HttpServletResponse) response; - final var ticket = httpRequest.getHeader("Authorization"); + try { + final var currentSubject = casAuthenticator.authenticate(httpRequest); - if (!ticketValidator.validateTicket(ticket)) { + final var authenticatedRequest = new AuthenticatedHttpServletRequestWrapper(httpRequest); + authenticatedRequest.addHeader("current-subject", currentSubject); + + chain.doFilter(authenticatedRequest, response); + } catch (final BadCredentialsException exc) { + // TODO.impl: should not be necessary if ResponseStatusException worked httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; } - - chain.doFilter(request, response); } } diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java new file mode 100644 index 00000000..2d0302ee --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java @@ -0,0 +1,54 @@ +package net.hostsharing.hsadminng.config; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import jakarta.servlet.http.HttpServletRequest; +import javax.xml.parsers.DocumentBuilderFactory; + +@Service +@NoArgsConstructor +@AllArgsConstructor +public class CasAuthenticator { + + @Value("${hsadminng.cas.server-url}") + private String casServerUrl; + + @Value("${hsadminng.cas.service-url}") + private String serviceUrl; + + private final RestTemplate restTemplate = new RestTemplate(); + + @SneakyThrows + public String authenticate(final HttpServletRequest httpRequest) { + // FIXME: create FakeCasAuthenticator + if (casServerUrl.equals("fake")) { + return httpRequest.getHeader("current-subject"); + } + + final var ticket = httpRequest.getHeader("Authorization"); + final var url = casServerUrl + "/p3/serviceValidate" + + "?service=" + serviceUrl + + "&ticket=" + ticket; + + final var response = restTemplate.getForObject(url, String.class); + + final var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() + .parse(new java.io.ByteArrayInputStream(response.getBytes())); + if ( doc.getElementsByTagName("cas:authenticationSuccess").getLength() == 0 ) { + // TODO.impl: for unknown reasons, this results in a 403 FORBIDDEN + // throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "CAS service ticket could not be validated"); + throw new BadCredentialsException("CAS service ticket could not be validated"); + } + final var authentication = new UsernamePasswordAuthenticationToken("test-user-from-authenticate", null, null); // TODO + SecurityContextHolder.getContext().setAuthentication(authentication); + return authentication.getName(); + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java b/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java deleted file mode 100644 index 18f5a52a..00000000 --- a/src/main/java/net/hostsharing/hsadminng/config/CasServiceTicketValidator.java +++ /dev/null @@ -1,40 +0,0 @@ -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")) { - 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; - } -} diff --git a/src/main/java/net/hostsharing/hsadminng/ping/PingController.java b/src/main/java/net/hostsharing/hsadminng/ping/PingController.java index f6f92f52..6ac6ff41 100644 --- a/src/main/java/net/hostsharing/hsadminng/ping/PingController.java +++ b/src/main/java/net/hostsharing/hsadminng/ping/PingController.java @@ -1,16 +1,22 @@ package net.hostsharing.hsadminng.ping; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; +import jakarta.validation.constraints.NotNull; + @Controller public class PingController { @ResponseBody @RequestMapping(value = "/api/ping", method = RequestMethod.GET) - public String ping() { - return "pong\n"; + public String ping( + @RequestHeader(name = "current-subject") @NotNull String currentSubject, + @RequestHeader(name = "assumed-roles", required = false) String assumedRoles + ) { + return "pong " + currentSubject + "\n"; } } diff --git a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java index 14940868..0ec4c5ac 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java @@ -1,18 +1,22 @@ package net.hostsharing.hsadminng.config; +import com.github.tomakehurst.wiremock.WireMockServer; 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.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; - +import static com.github.tomakehurst.wiremock.client.WireMock.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@TestPropertySource(properties = {"server.port=0"}) +@TestPropertySource(properties = {"server.port=0", "hsadminng.cas.server-url=http://localhost:8088/cas"}) // IMPORTANT: To test prod config, do not use test profile! class CasAuthenticationFilterIntegrationTest { @@ -22,10 +26,63 @@ class CasAuthenticationFilterIntegrationTest { @Autowired private TestRestTemplate restTemplate; + @Autowired + private WireMockServer wireMockServer; + + @Test + public void shouldAcceptRequest() { + // given + wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=http://localhost:8080/api&ticket=valid")) + .willReturn(aResponse() + .withStatus(200) + .withBody(""" + + + test-user + + + """))); + + // when + final var result = restTemplate.exchange( + "http://localhost:" + this.serverPort + "/api/ping", + HttpMethod.GET, + new HttpEntity<>(null, headers("Authorization", "valid")), + String.class + ); + + // then + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody()).isEqualTo("pong test-user-from-authenticate\n"); + } + @Test public void shouldRejectRequest() { - final var result = this.restTemplate.getForEntity( - "http://localhost:" + this.serverPort + "/api/ping", String.class); + // given + wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=http://localhost:8080/api&ticket=invalid")) + .willReturn(aResponse() + .withStatus(200) + .withBody(""" + + + + """))); + + // when + final var result = restTemplate.exchange( + "http://localhost:" + this.serverPort + "/api/ping", + HttpMethod.GET, + new HttpEntity<>(null, headers("Authorization", "invalid")), + String.class + ); + + // then assertThat(result.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } + + private HttpHeaders headers(final String key, final String value) { + final var headers = new HttpHeaders(); + headers.set(key, value); + return headers; + } } diff --git a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java index de8c63ea..6c75cbc2 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java @@ -40,7 +40,7 @@ class WebSecurityConfigIntegrationTest { final var result = restTemplate.exchange( "http://localhost:" + this.serverPort + "/api/ping", HttpMethod.GET, - new HttpEntity(null, headers), + new HttpEntity<>(null, headers), String.class ); diff --git a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java index b0def144..16918eef 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java +++ b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.test; +import net.hostsharing.hsadminng.config.CasAuthenticator; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -16,4 +17,9 @@ public class DisableSecurityConfig { .csrf(AbstractHttpConfigurer::disable); return http.build(); } + + @Bean + public CasAuthenticator casServiceTicketValidator() { + return new CasAuthenticator("fake", null); + } } diff --git a/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java b/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java new file mode 100644 index 00000000..814a798f --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java @@ -0,0 +1,19 @@ +package net.hostsharing.hsadminng.test; + +import com.github.tomakehurst.wiremock.WireMockServer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class WireMockConfig { + + private static final WireMockServer wireMockServer = new WireMockServer(8088); // Use a different port to avoid conflicts + + @Bean + public WireMockServer wireMockServer() { + if (!wireMockServer.isRunning()) { + wireMockServer.start(); + } + return wireMockServer; + } +} -- 2.39.5 From d359ae1693ea0451f6cf3334404789e3cba6ceae Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 12:30:50 +0100 Subject: [PATCH 05/24] introduce interface Authenticator --- .../hsadminng/config/Authenticator.java | 8 ++++++++ .../hsadminng/config/CasAuthenticator.java | 7 +------ .../hsadminng/test/DisableSecurityConfig.java | 5 +++-- .../hsadminng/test/FakeAuthenticator.java | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/config/Authenticator.java create mode 100644 src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java diff --git a/src/main/java/net/hostsharing/hsadminng/config/Authenticator.java b/src/main/java/net/hostsharing/hsadminng/config/Authenticator.java new file mode 100644 index 00000000..13f4ada4 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/config/Authenticator.java @@ -0,0 +1,8 @@ +package net.hostsharing.hsadminng.config; + +import jakarta.servlet.http.HttpServletRequest; + +public interface Authenticator { + + String authenticate(final HttpServletRequest httpRequest); +} diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java index 2d0302ee..a048a16c 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java @@ -16,7 +16,7 @@ import javax.xml.parsers.DocumentBuilderFactory; @Service @NoArgsConstructor @AllArgsConstructor -public class CasAuthenticator { +public class CasAuthenticator implements Authenticator { @Value("${hsadminng.cas.server-url}") private String casServerUrl; @@ -28,11 +28,6 @@ public class CasAuthenticator { @SneakyThrows public String authenticate(final HttpServletRequest httpRequest) { - // FIXME: create FakeCasAuthenticator - if (casServerUrl.equals("fake")) { - return httpRequest.getHeader("current-subject"); - } - final var ticket = httpRequest.getHeader("Authorization"); final var url = casServerUrl + "/p3/serviceValidate" + "?service=" + serviceUrl + diff --git a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java index 16918eef..978edbb8 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java +++ b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.test; +import net.hostsharing.hsadminng.config.Authenticator; import net.hostsharing.hsadminng.config.CasAuthenticator; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; @@ -19,7 +20,7 @@ public class DisableSecurityConfig { } @Bean - public CasAuthenticator casServiceTicketValidator() { - return new CasAuthenticator("fake", null); + public Authenticator casServiceTicketValidator() { + return new FakeAuthenticator(); } } diff --git a/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java b/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java new file mode 100644 index 00000000..66dcd9a3 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java @@ -0,0 +1,18 @@ +package net.hostsharing.hsadminng.test; + +import lombok.SneakyThrows; +import net.hostsharing.hsadminng.config.Authenticator; +import net.hostsharing.hsadminng.config.CasAuthenticator; +import org.springframework.stereotype.Service; + +import jakarta.servlet.http.HttpServletRequest; + +@Service +public class FakeAuthenticator implements Authenticator { + + @Override + @SneakyThrows + public String authenticate(final HttpServletRequest httpRequest) { + return httpRequest.getHeader("current-subject"); + } +} -- 2.39.5 From 69ab43a01c9c7eb9e65609dfe07fe2175d5e02ec Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 12:31:07 +0100 Subject: [PATCH 06/24] add missing wiremock lib --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index b409fc37..dbc43589 100644 --- a/build.gradle +++ b/build.gradle @@ -93,6 +93,7 @@ dependencies { testImplementation 'org.hamcrest:hamcrest-core:3.0' testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1' testImplementation 'org.junit.jupiter:junit-jupiter-api' + testImplementation 'org.wiremock:wiremock-standalone:3.10.0' } dependencyManagement { -- 2.39.5 From 9ed2b79fe3d7d75cddd1fc61e2bdb4d5127cd067 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 13:14:30 +0100 Subject: [PATCH 07/24] fix ping test --- README.md | 2 ++ ...nFilter.java => AuthenticationFilter.java} | 6 ++--- .../hsadminng/config/CasAuthenticator.java | 2 ++ .../WebSecurityConfigIntegrationTest.java | 26 ++++++++++++++++--- .../hsadminng/test/DisableSecurityConfig.java | 1 - .../hsadminng/test/FakeAuthenticator.java | 1 - 6 files changed, 30 insertions(+), 8 deletions(-) rename src/main/java/net/hostsharing/hsadminng/config/{CasAuthenticationFilter.java => AuthenticationFilter.java} (87%) diff --git a/README.md b/README.md index 6be7fbe3..5ade5549 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ If you have at least Docker and the Java JDK installed in appropriate versions a gw bootRun # compiles and runs the application on localhost:8080 + FIXME: use bin/hsadmin-ng for the following commands + # the following command should reply with "pong": curl -f http://localhost:8080/api/ping diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java b/src/main/java/net/hostsharing/hsadminng/config/AuthenticationFilter.java similarity index 87% rename from src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java rename to src/main/java/net/hostsharing/hsadminng/config/AuthenticationFilter.java index 404a4027..1849b815 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticationFilter.java +++ b/src/main/java/net/hostsharing/hsadminng/config/AuthenticationFilter.java @@ -13,10 +13,10 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.stereotype.Component; @Component -public class CasAuthenticationFilter implements Filter { +public class AuthenticationFilter implements Filter { @Autowired - private CasAuthenticator casAuthenticator; + private Authenticator authenticator; @Override @SneakyThrows @@ -25,7 +25,7 @@ public class CasAuthenticationFilter implements Filter { final var httpResponse = (HttpServletResponse) response; try { - final var currentSubject = casAuthenticator.authenticate(httpRequest); + final var currentSubject = authenticator.authenticate(httpRequest); final var authenticatedRequest = new AuthenticatedHttpServletRequestWrapper(httpRequest); authenticatedRequest.addHeader("current-subject", currentSubject); diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java index a048a16c..a7918324 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java @@ -4,6 +4,7 @@ import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -13,6 +14,7 @@ import org.springframework.web.client.RestTemplate; import jakarta.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilderFactory; +@Primary @Service @NoArgsConstructor @AllArgsConstructor diff --git a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java index 6c75cbc2..d775cfde 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.config; import java.util.Map; +import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -14,10 +15,13 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.test.context.TestPropertySource; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@TestPropertySource(properties = {"management.port=0", "server.port=0", "hsadminng.cas.server-url=fake"}) +@TestPropertySource(properties = {"management.port=0", "server.port=0", "hsadminng.cas.server-url=http://localhost:8088/cas"}) // IMPORTANT: To test prod config, do not use test profile! class WebSecurityConfigIntegrationTest { @@ -30,11 +34,27 @@ class WebSecurityConfigIntegrationTest { @Autowired private TestRestTemplate restTemplate; + @Autowired + private WireMockServer wireMockServer; + @Test public void shouldSupportPingEndpoint() { + // given + wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=http://localhost:8080/api&ticket=test-user")) + .willReturn(aResponse() + .withStatus(200) + .withBody(""" + + + test-user + + + """))); + + // fake Authorization header final var headers = new HttpHeaders(); - headers.set("Authorization", "test"); + headers.set("Authorization", "test-user"); // http request final var result = restTemplate.exchange( @@ -45,7 +65,7 @@ class WebSecurityConfigIntegrationTest { ); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).startsWith("pong"); + assertThat(result.getBody()).startsWith("pong test-user"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java index 978edbb8..0fa84c8c 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java +++ b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java @@ -1,7 +1,6 @@ package net.hostsharing.hsadminng.test; import net.hostsharing.hsadminng.config.Authenticator; -import net.hostsharing.hsadminng.config.CasAuthenticator; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; diff --git a/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java b/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java index 66dcd9a3..f749c262 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java +++ b/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java @@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.test; import lombok.SneakyThrows; import net.hostsharing.hsadminng.config.Authenticator; -import net.hostsharing.hsadminng.config.CasAuthenticator; import org.springframework.stereotype.Service; import jakarta.servlet.http.HttpServletRequest; -- 2.39.5 From a41430eea9bbd076526785816954a1fe85e857d0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 14:22:52 +0100 Subject: [PATCH 08/24] fi most tests --- .../hostsharing/hsadminng/config/CasAuthenticator.java | 8 -------- .../hostsharing/hsadminng/config/WebSecurityConfig.java | 7 +++++++ .../hostsharing/hsadminng/test/DisableSecurityConfig.java | 5 ++++- .../net/hostsharing/hsadminng/test/FakeAuthenticator.java | 2 -- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java index a7918324..278b5470 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java @@ -1,23 +1,15 @@ package net.hostsharing.hsadminng.config; -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Primary; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import jakarta.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilderFactory; -@Primary -@Service -@NoArgsConstructor -@AllArgsConstructor public class CasAuthenticator implements Authenticator { @Value("${hsadminng.cas.server-url}") diff --git a/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java b/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java index 6da383ab..6c91ce17 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java +++ b/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java @@ -24,4 +24,11 @@ public class WebSecurityConfig { ) .build(); } + + @Bean + @Profile("!test") + public Authenticator casServiceTicketValidator() { + return new CasAuthenticator(); + } + } diff --git a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java index 0fa84c8c..0021dbbe 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java +++ b/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.test; import net.hostsharing.hsadminng.config.Authenticator; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; @@ -11,6 +12,7 @@ import org.springframework.security.web.SecurityFilterChain; public class DisableSecurityConfig { @Bean + @Profile("test") public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) @@ -19,7 +21,8 @@ public class DisableSecurityConfig { } @Bean - public Authenticator casServiceTicketValidator() { + @Profile("test") + public Authenticator fakeAuthenticator() { return new FakeAuthenticator(); } } diff --git a/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java b/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java index f749c262..4e5e3132 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java +++ b/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java @@ -2,11 +2,9 @@ package net.hostsharing.hsadminng.test; import lombok.SneakyThrows; import net.hostsharing.hsadminng.config.Authenticator; -import org.springframework.stereotype.Service; import jakarta.servlet.http.HttpServletRequest; -@Service public class FakeAuthenticator implements Authenticator { @Override -- 2.39.5 From 07dcf96ee5fde0cddb06557804b884fa30e63d38 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 15:46:35 +0100 Subject: [PATCH 09/24] don't wire Wiremock in scenariotests --- .../config/CasAuthenticationFilterIntegrationTest.java | 3 ++- .../hsadminng/config/WebSecurityConfigIntegrationTest.java | 3 ++- .../hs/booking/item/HsBookingItemControllerRestTest.java | 2 ++ .../hs/hosting/asset/HsHostingAssetControllerRestTest.java | 2 ++ .../java/net/hostsharing/hsadminng/test/WireMockConfig.java | 4 +++- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java index 0ec4c5ac..3cb13840 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java @@ -10,6 +10,7 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; @@ -17,7 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static com.github.tomakehurst.wiremock.client.WireMock.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestPropertySource(properties = {"server.port=0", "hsadminng.cas.server-url=http://localhost:8088/cas"}) -// IMPORTANT: To test prod config, do not use test profile! +@ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile! class CasAuthenticationFilterIntegrationTest { @Value("${local.server.port}") diff --git a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java index d775cfde..65f5fc95 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java @@ -13,6 +13,7 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; @@ -22,7 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestPropertySource(properties = {"management.port=0", "server.port=0", "hsadminng.cas.server-url=http://localhost:8088/cas"}) -// IMPORTANT: To test prod config, do not use test profile! +@ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile! class WebSecurityConfigIntegrationTest { @Value("${local.server.port}") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java index 77e0abc8..e5f46fe3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -41,6 +42,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(HsBookingItemController.class) @Import({StrictMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class}) @RunWith(SpringRunner.class) +@ActiveProfiles("test") class HsBookingItemControllerRestTest { @Autowired diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index 5aad9e2f..7af8643f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -24,6 +24,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(HsHostingAssetController.class) @Import({ StandardMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class }) @RunWith(SpringRunner.class) +@ActiveProfiles("test") public class HsHostingAssetControllerRestTest { @Autowired diff --git a/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java b/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java index 814a798f..62264b77 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java +++ b/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java @@ -3,11 +3,13 @@ package net.hostsharing.hsadminng.test; import com.github.tomakehurst.wiremock.WireMockServer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; @Configuration +@Profile("wiremock") public class WireMockConfig { - private static final WireMockServer wireMockServer = new WireMockServer(8088); // Use a different port to avoid conflicts + private static final WireMockServer wireMockServer = new WireMockServer(8088); @Bean public WireMockServer wireMockServer() { -- 2.39.5 From 2c1a5f59332ac2a67fc68ae75ad24458446bed4a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 16:44:06 +0100 Subject: [PATCH 10/24] fetch username from CAS validation response --- .../hostsharing/hsadminng/config/CasAuthenticator.java | 5 +++-- .../config/CasAuthenticationFilterIntegrationTest.java | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java index 278b5470..aa580fbf 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java @@ -31,12 +31,13 @@ public class CasAuthenticator implements Authenticator { final var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() .parse(new java.io.ByteArrayInputStream(response.getBytes())); - if ( doc.getElementsByTagName("cas:authenticationSuccess").getLength() == 0 ) { + if (doc.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) { // TODO.impl: for unknown reasons, this results in a 403 FORBIDDEN // throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "CAS service ticket could not be validated"); throw new BadCredentialsException("CAS service ticket could not be validated"); } - final var authentication = new UsernamePasswordAuthenticationToken("test-user-from-authenticate", null, null); // TODO + final var userName = doc.getElementsByTagName("cas:user").item(0).getTextContent(); + final var authentication = new UsernamePasswordAuthenticationToken(userName, null, null); SecurityContextHolder.getContext().setAuthentication(authentication); return authentication.getName(); } diff --git a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java index 3cb13840..c470ba4a 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java @@ -13,7 +13,7 @@ import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; - +import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; import static com.github.tomakehurst.wiremock.client.WireMock.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -33,16 +33,18 @@ class CasAuthenticationFilterIntegrationTest { @Test public void shouldAcceptRequest() { // given + final var username = "test-user-" + randomAlphanumeric(4); wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=http://localhost:8080/api&ticket=valid")) .willReturn(aResponse() .withStatus(200) .withBody(""" - test-user + %{username} - """))); + """.replace("%{username}", username) + ))); // when final var result = restTemplate.exchange( @@ -54,7 +56,7 @@ class CasAuthenticationFilterIntegrationTest { // then assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).isEqualTo("pong test-user-from-authenticate\n"); + assertThat(result.getBody()).isEqualTo("pong " + username + "\n"); } @Test -- 2.39.5 From 3f7df2bc81620849e33141a33398f6c6d42bb1e1 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 16:54:42 +0100 Subject: [PATCH 11/24] remove authentication from ScenarioTests --- .../java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java index fc8000c7..01c5dede 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java @@ -160,7 +160,6 @@ public abstract class UseCase> { .GET() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -176,7 +175,6 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -192,7 +190,6 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -207,7 +204,6 @@ public abstract class UseCase> { .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") .header("current-subject", ScenarioTest.RUN_AS_USER) - .header("Authorization", "test") // FIXME: encode current-subject .timeout(seconds(10)) .build(); final var response = client.send(request, BodyHandlers.ofString()); -- 2.39.5 From 57a710fc240e004eb7fd247a56efe5356e94a8f9 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 20 Dec 2024 17:19:09 +0100 Subject: [PATCH 12/24] refactor to avoid dependency cycles --- .../config/CustomActuatorEndpointAcceptanceTest.java | 3 +-- .../hsadminng/{test => config}/DisableSecurityConfig.java | 3 +-- .../hsadminng/{test => config}/FakeAuthenticator.java | 3 +-- .../hsadminng/{test => config}/WireMockConfig.java | 2 +- .../booking/item/HsBookingItemControllerAcceptanceTest.java | 4 ++-- .../hs/booking/item/HsBookingItemControllerRestTest.java | 4 ++-- .../project/HsBookingProjectControllerAcceptanceTest.java | 4 ++-- .../asset/HsHostingAssetControllerAcceptanceTest.java | 6 +++--- .../hs/hosting/asset/HsHostingAssetControllerRestTest.java | 4 ++-- .../asset/HsHostingAssetPropsControllerAcceptanceTest.java | 4 ++-- .../HsOfficeBankAccountControllerAcceptanceTest.java | 4 ++-- .../bankaccount/HsOfficeBankAccountControllerRestTest.java | 2 +- .../contact/HsOfficeContactControllerAcceptanceTest.java | 4 ++-- ...OfficeCoopAssetsTransactionControllerAcceptanceTest.java | 4 ++-- .../HsOfficeCoopAssetsTransactionControllerRestTest.java | 4 ++-- ...OfficeCoopSharesTransactionControllerAcceptanceTest.java | 4 ++-- .../HsOfficeCoopSharesTransactionControllerRestTest.java | 2 +- .../debitor/HsOfficeDebitorControllerAcceptanceTest.java | 4 ++-- .../HsOfficeMembershipControllerAcceptanceTest.java | 4 ++-- .../membership/HsOfficeMembershipControllerRestTest.java | 4 ++-- .../partner/HsOfficePartnerControllerAcceptanceTest.java | 4 ++-- .../office/partner/HsOfficePartnerControllerRestTest.java | 2 +- .../person/HsOfficePersonControllerAcceptanceTest.java | 4 ++-- .../relation/HsOfficeRelationControllerAcceptanceTest.java | 4 ++-- .../hs/office/scenarios/HsOfficeScenarioTests.java | 2 +- .../HsOfficeSepaMandateControllerAcceptanceTest.java | 4 ++-- .../rbac/grant/RbacGrantControllerAcceptanceTest.java | 2 +- .../rbac/role/RbacRoleControllerAcceptanceTest.java | 2 +- .../hsadminng/rbac/role/RbacRoleControllerRestTest.java | 2 +- .../rbac/subject/RbacSubjectControllerAcceptanceTest.java | 2 +- .../rbac/subject/RbacSubjectControllerRestTest.java | 2 +- .../test/cust/TestCustomerControllerAcceptanceTest.java | 2 +- .../rbac/test/pac/TestPackageControllerAcceptanceTest.java | 2 +- .../hostsharing/hsadminng/{rbac => }/test/JsonMatcher.java | 2 +- 34 files changed, 53 insertions(+), 56 deletions(-) rename src/test/java/net/hostsharing/hsadminng/{test => config}/DisableSecurityConfig.java (90%) rename src/test/java/net/hostsharing/hsadminng/{test => config}/FakeAuthenticator.java (76%) rename src/test/java/net/hostsharing/hsadminng/{test => config}/WireMockConfig.java (93%) rename src/test/java/net/hostsharing/hsadminng/{rbac => }/test/JsonMatcher.java (98%) diff --git a/src/test/java/net/hostsharing/hsadminng/config/CustomActuatorEndpointAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/config/CustomActuatorEndpointAcceptanceTest.java index 1509831e..251804e2 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/CustomActuatorEndpointAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/CustomActuatorEndpointAcceptanceTest.java @@ -2,13 +2,12 @@ package net.hostsharing.hsadminng.config; import io.restassured.RestAssured; import net.hostsharing.hsadminng.HsadminNgApplication; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalManagementPort; import org.springframework.test.context.ActiveProfiles; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, diff --git a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java b/src/test/java/net/hostsharing/hsadminng/config/DisableSecurityConfig.java similarity index 90% rename from src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java rename to src/test/java/net/hostsharing/hsadminng/config/DisableSecurityConfig.java index 0021dbbe..4c4f98e8 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/DisableSecurityConfig.java +++ b/src/test/java/net/hostsharing/hsadminng/config/DisableSecurityConfig.java @@ -1,6 +1,5 @@ -package net.hostsharing.hsadminng.test; +package net.hostsharing.hsadminng.config; -import net.hostsharing.hsadminng.config.Authenticator; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; diff --git a/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java b/src/test/java/net/hostsharing/hsadminng/config/FakeAuthenticator.java similarity index 76% rename from src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java rename to src/test/java/net/hostsharing/hsadminng/config/FakeAuthenticator.java index 4e5e3132..139ef053 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/FakeAuthenticator.java +++ b/src/test/java/net/hostsharing/hsadminng/config/FakeAuthenticator.java @@ -1,7 +1,6 @@ -package net.hostsharing.hsadminng.test; +package net.hostsharing.hsadminng.config; import lombok.SneakyThrows; -import net.hostsharing.hsadminng.config.Authenticator; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java b/src/test/java/net/hostsharing/hsadminng/config/WireMockConfig.java similarity index 93% rename from src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java rename to src/test/java/net/hostsharing/hsadminng/config/WireMockConfig.java index 62264b77..04f2371b 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/WireMockConfig.java +++ b/src/test/java/net/hostsharing/hsadminng/config/WireMockConfig.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.test; +package net.hostsharing.hsadminng.config; import com.github.tomakehurst.wiremock.WireMockServer; import org.springframework.context.annotation.Bean; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java index 120f62f5..4715ad72 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerAcceptanceTest.java @@ -12,7 +12,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealRepository; import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.ClassOrderer; @@ -38,7 +38,7 @@ import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.matchesRegex; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java index e5f46fe3..7c0c68b0 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java @@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository; import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -30,7 +30,7 @@ import java.time.LocalDate; import java.util.Map; import java.util.UUID; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.hamcrest.Matchers.matchesRegex; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectControllerAcceptanceTest.java index 737cc0a2..05313c56 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/project/HsBookingProjectControllerAcceptanceTest.java @@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -19,7 +19,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import java.util.UUID; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.matchesRegex; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java index 2d493fd0..9055148e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java @@ -14,7 +14,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Nested; @@ -37,8 +37,8 @@ import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.EMAI import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.strictlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.strictlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.matchesRegex; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index 7af8643f..ffc97a63 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -11,7 +11,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository; import net.hostsharing.hsadminng.mapper.Array; import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -44,7 +44,7 @@ import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.MANAGE import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetTestEntities.MANAGED_SERVER_HOSTING_ASSET_REAL_TEST_ENTITY; import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetTestEntities.MANAGED_WEBSPACE_HOSTING_ASSET_REAL_TEST_ENTITY; import static net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealTestEntity.TEST_REAL_CONTACT; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java index b20a3c45..de7dd82d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropsControllerAcceptanceTest.java @@ -3,13 +3,13 @@ package net.hostsharing.hsadminng.hs.hosting.asset; import io.restassured.RestAssured; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.ActiveProfiles; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java index 0a565a60..1ce84ba9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java @@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.apache.commons.lang3.RandomStringUtils; import org.json.JSONException; import org.junit.jupiter.api.*; @@ -21,7 +21,7 @@ import jakarta.persistence.PersistenceContext; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java index 7a699a25..a83558d6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerRestTest.java @@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.hs.office.bankaccount; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.mapper.StandardMapper; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactControllerAcceptanceTest.java index 645dea0e..a4b19eef 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactControllerAcceptanceTest.java @@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.apache.commons.lang3.RandomStringUtils; import org.json.JSONException; import org.junit.jupiter.api.AfterEach; @@ -27,7 +27,7 @@ import java.util.concurrent.ThreadLocalRandom; import static java.util.Map.entry; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index 1239c2a4..cceee9dd 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -7,7 +7,7 @@ import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -26,7 +26,7 @@ import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.DEPOSIT; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.startsWith; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java index c064a848..b9cb7aff 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java @@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.rbac.test.JsonBuilder; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.test.TestUuidGenerator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,7 +38,7 @@ import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsT import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.REVERSAL; import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.TRANSFER; import static net.hostsharing.hsadminng.rbac.test.JsonBuilder.jsonObject; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; import static org.hamcrest.Matchers.is; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index 844e8db5..5568fa2f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -7,7 +7,7 @@ import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -25,7 +25,7 @@ import java.time.LocalDate; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.startsWith; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java index 2b0129a3..5bc432de 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerRestTest.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.rbac.test.JsonBuilder; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index a05687e4..ca9e7c84 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -12,7 +12,7 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -29,7 +29,7 @@ import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index b0f99593..88f29868 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.json.JSONException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; @@ -27,7 +27,7 @@ import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipStatus.ACTIVE; import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipStatus.CANCELLED; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.*; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index 39af92bc..58271f57 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransact import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -24,7 +24,7 @@ import java.util.Optional; import java.util.UUID; import static io.hypersistence.utils.hibernate.type.range.Range.localDateRange; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java index e5ea3cca..941d2917 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java @@ -13,7 +13,7 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealReposito import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -25,7 +25,7 @@ import java.util.UUID; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.EX_PARTNER; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.*; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java index 4d016725..100d3852 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java @@ -7,7 +7,7 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java index 15307e4e..b0cae150 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java @@ -6,7 +6,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; @@ -22,7 +22,7 @@ import jakarta.persistence.PersistenceContext; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.*; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java index d79ddbfa..c1ce47e4 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java @@ -9,7 +9,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -21,7 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index f3037336..2f4ee9e5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -39,7 +39,7 @@ import net.hostsharing.hsadminng.hs.scenarios.Produces; import net.hostsharing.hsadminng.hs.scenarios.Requires; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension; import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Disabled; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index ff5c4e46..ef334b0a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountReposi import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -26,7 +26,7 @@ import java.util.UUID; import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid; -import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.*; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantControllerAcceptanceTest.java index e2460d05..8f059572 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/grant/RbacGrantControllerAcceptanceTest.java @@ -10,7 +10,7 @@ import net.hostsharing.hsadminng.rbac.role.RbacRoleRepository; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectEntity; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerAcceptanceTest.java index ac925f2e..aa8c7727 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerAcceptanceTest.java @@ -4,7 +4,7 @@ import io.restassured.RestAssured; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java index f7507be1..d79a5578 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.rbac.role; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerAcceptanceTest.java index 9392cc6c..45d8dac8 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerAcceptanceTest.java @@ -5,7 +5,7 @@ import io.restassured.http.ContentType; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java index e788c34b..917cd7ff 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.rbac.subject; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.mapper.StandardMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerControllerAcceptanceTest.java index a022003b..1c210164 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/test/cust/TestCustomerControllerAcceptanceTest.java @@ -5,7 +5,7 @@ import io.restassured.http.ContentType; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageControllerAcceptanceTest.java index 2ba2f086..4d68422e 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/test/pac/TestPackageControllerAcceptanceTest.java @@ -4,7 +4,7 @@ import io.restassured.RestAssured; import io.restassured.http.ContentType; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.test.DisableSecurityConfig; +import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/JsonMatcher.java b/src/test/java/net/hostsharing/hsadminng/test/JsonMatcher.java similarity index 98% rename from src/test/java/net/hostsharing/hsadminng/rbac/test/JsonMatcher.java rename to src/test/java/net/hostsharing/hsadminng/test/JsonMatcher.java index 22ddead9..cf98f08b 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/JsonMatcher.java +++ b/src/test/java/net/hostsharing/hsadminng/test/JsonMatcher.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.rbac.test; +package net.hostsharing.hsadminng.test; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -- 2.39.5 From 3a4f0685289c3473913aa1586a10da42ba0dc6a4 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 21 Dec 2024 14:24:41 +0100 Subject: [PATCH 13/24] implement bypassing CAS-authentication and adding admin accounts --- .aliases | 2 + README.md | 43 ++++++++++++++++--- .../hsadminng/config/CasAuthenticator.java | 35 ++++++++++++--- src/main/resources/application.yml | 4 +- ...asAuthenticationFilterIntegrationTest.java | 17 +++----- .../config/CasAuthenticatorUnitTest.java | 28 ++++++++++++ .../hsadminng/config/HttpHeadersBuilder.java | 12 ++++++ .../WebSecurityConfigIntegrationTest.java | 7 ++- src/test/resources/application.yml | 4 +- 9 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/config/CasAuthenticatorUnitTest.java create mode 100644 src/test/java/net/hostsharing/hsadminng/config/HttpHeadersBuilder.java diff --git a/.aliases b/.aliases index 2fb7e74e..5f200e79 100644 --- a/.aliases +++ b/.aliases @@ -93,6 +93,8 @@ alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResource alias gw-test='. .aliases; ./gradlew test' alias gw-check='. .aliases; gw test check -x pitest' +alias hsadmin-ng='bin/hsadmin-ng' + # etc/docker-compose.yml limits CPUs+MEM and includes a PostgreSQL config for analysing slow queries alias gw-importOfficeData-in-docker-compose=' docker-compose -f etc/docker-compose.yml down && diff --git a/README.md b/README.md index 5ade5549..1ca8e62e 100644 --- a/README.md +++ b/README.md @@ -67,31 +67,37 @@ If you have at least Docker and the Java JDK installed in appropriate versions a gw scenarioTests # compiles and scenario-tests - takes ~1min on a decent machine # if the container has not been built yet, run this: - pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432 + pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432 + # if the container has been built already and you want to keep the data, run this: pg-sql-start - gw bootRun # compiles and runs the application on localhost:8080 +Next, compile and run the application without CAS-authentication on `localhost:8080`: - FIXME: use bin/hsadmin-ng for the following commands + export HSADMINNG_CAS_SERVER= + gw bootRun + +For using the REST-API with CAS-authentication, see `bin/hsadmin-ng`. + +Now we can access the REST API, e.g. using curl: # the following command should reply with "pong": - curl -f http://localhost:8080/api/ping + curl -f -s http://localhost:8080/api/ping # the following command should return a JSON array with just all customers: - curl -f\ + curl -f -s\ -H 'current-subject: superuser-alex@hostsharing.net' \ http://localhost:8080/api/test/customers \ | jq # just if `jq` is installed, to prettyprint the output # the following command should return a JSON array with just all packages visible for the admin of the customer yyy: - curl -f\ + curl -f -s\ -H 'current-subject: superuser-alex@hostsharing.net' -H 'assumed-roles: rbactest.customer#yyy:ADMIN' \ http://localhost:8080/api/test/packages \ | jq # add a new customer - curl -f\ + curl -f -s\ -H 'current-subject: superuser-alex@hostsharing.net' -H "Content-Type: application/json" \ -d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \ -X POST http://localhost:8080/api/test/customers \ @@ -809,6 +815,29 @@ postgres-autodoc The output will list the generated files. +### How to Add (Real) Admin Users + +```sql +DO $$ +DECLARE + -- replace with your admin account names + admin_users TEXT[] := ARRAY['admin-1', 'admin-2', 'admin-3']; + admin TEXT; +BEGIN + -- run as superuser + call base.defineContext('adding real admin users', null, null, null); + + -- for all new admin accounts + FOREACH admin IN ARRAY admin_users LOOP + call rbac.grantRoleToSubjectUnchecked( + rbac.findRoleId(rbac.global_ADMIN()), -- granted by role + rbac.findRoleId(rbac.global_ADMIN()), -- role to grant + rbac.create_subject(admin)); -- creates the new admin account + END LOOP; +END $$; +``` + + ## Further Documentation - the `doc` directory contains architecture concepts and a glossary diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java index aa580fbf..4a4dd663 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java @@ -1,27 +1,50 @@ package net.hostsharing.hsadminng.config; import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.client.RestTemplate; +import org.xml.sax.SAXException; import jakarta.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; public class CasAuthenticator implements Authenticator { - @Value("${hsadminng.cas.server-url}") + @Value("${hsadminng.cas.server}") private String casServerUrl; - @Value("${hsadminng.cas.service-url}") + @Value("${hsadminng.cas.service}") private String serviceUrl; private final RestTemplate restTemplate = new RestTemplate(); @SneakyThrows public String authenticate(final HttpServletRequest httpRequest) { + final var userName = StringUtils.isBlank(casServerUrl) + ? bypassCurrentSubject(httpRequest) + : casValidation(httpRequest); + final var authentication = new UsernamePasswordAuthenticationToken(userName, null, null); + SecurityContextHolder.getContext().setAuthentication(authentication); + return authentication.getName(); + } + + private static String bypassCurrentSubject(final HttpServletRequest httpRequest) { + final var userName = httpRequest.getHeader("current-subject"); + System.err.println("CasAuthenticator.bypassCurrentSubject: " + userName); + return userName; + } + + private String casValidation(final HttpServletRequest httpRequest) + throws SAXException, IOException, ParserConfigurationException { + + System.err.println("CasAuthenticator.casValidation using CAS-server: " + casServerUrl); + final var ticket = httpRequest.getHeader("Authorization"); final var url = casServerUrl + "/p3/serviceValidate" + "?service=" + serviceUrl + @@ -34,11 +57,13 @@ public class CasAuthenticator implements Authenticator { if (doc.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) { // TODO.impl: for unknown reasons, this results in a 403 FORBIDDEN // throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "CAS service ticket could not be validated"); + System.err.println("CAS service ticket could not be validated"); + System.err.println("CAS-validation-URL: " + url); + System.err.println(response); throw new BadCredentialsException("CAS service ticket could not be validated"); } final var userName = doc.getElementsByTagName("cas:user").item(0).getTextContent(); - final var authentication = new UsernamePasswordAuthenticationToken(userName, null, null); - SecurityContextHolder.getContext().setAuthentication(authentication); - return authentication.getName(); + System.err.println("CAS-user: " + userName); + return "superuser-alex@hostsharing.net"; // userName; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 054973b2..69ad1e1b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -37,8 +37,8 @@ hsadminng: postgres: leakproof: cas: - server-url: https://cas.example.com/cas - service-url: http://localhost:8080/api # TODO.conf: deployment target + server: https://login.hostsharing.net/cas # use empty string to bypass CAS-validation and directly use current-subject + service: https://hsadminng.hostsharing.net:443 # TODO.conf: deployment target + matching CAS service ID metrics: distribution: diff --git a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java index c470ba4a..0c705e25 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticationFilterIntegrationTest.java @@ -7,23 +7,26 @@ 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.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; +import static net.hostsharing.hsadminng.config.HttpHeadersBuilder.headers; import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; import static com.github.tomakehurst.wiremock.client.WireMock.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@TestPropertySource(properties = {"server.port=0", "hsadminng.cas.server-url=http://localhost:8088/cas"}) +@TestPropertySource(properties = "server.port=0") @ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile! class CasAuthenticationFilterIntegrationTest { @Value("${local.server.port}") private int serverPort; + @Value("${hsadminng.cas.service}") + private String serviceUrl; + @Autowired private TestRestTemplate restTemplate; @@ -34,7 +37,7 @@ class CasAuthenticationFilterIntegrationTest { public void shouldAcceptRequest() { // given final var username = "test-user-" + randomAlphanumeric(4); - wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=http://localhost:8080/api&ticket=valid")) + wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=valid")) .willReturn(aResponse() .withStatus(200) .withBody(""" @@ -62,7 +65,7 @@ class CasAuthenticationFilterIntegrationTest { @Test public void shouldRejectRequest() { // given - wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=http://localhost:8080/api&ticket=invalid")) + wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=invalid")) .willReturn(aResponse() .withStatus(200) .withBody(""" @@ -82,10 +85,4 @@ class CasAuthenticationFilterIntegrationTest { // then assertThat(result.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } - - private HttpHeaders headers(final String key, final String value) { - final var headers = new HttpHeaders(); - headers.set(key, value); - return headers; - } } diff --git a/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticatorUnitTest.java new file mode 100644 index 00000000..5691e092 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/config/CasAuthenticatorUnitTest.java @@ -0,0 +1,28 @@ +package net.hostsharing.hsadminng.config; + +import org.junit.jupiter.api.Test; + +import jakarta.servlet.http.HttpServletRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +class CasAuthenticatorUnitTest { + + final CasAuthenticator casAuthenticator = new CasAuthenticator(); + + @Test + void bypassesAuthenticationIfNoCasServerIsConfigured() { + + // given + final var request = mock(HttpServletRequest.class); + given(request.getHeader("current-subject")).willReturn("given-user"); + + // when + final var userName = casAuthenticator.authenticate(request); + + // then + assertThat(userName).isEqualTo("given-user"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/config/HttpHeadersBuilder.java b/src/test/java/net/hostsharing/hsadminng/config/HttpHeadersBuilder.java new file mode 100644 index 00000000..ac61fb35 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/config/HttpHeadersBuilder.java @@ -0,0 +1,12 @@ +package net.hostsharing.hsadminng.config; + +import org.springframework.http.HttpHeaders; + +public class HttpHeadersBuilder { + + public static HttpHeaders headers(final String key, final String value) { + final var headers = new HttpHeaders(); + headers.set(key, value); + return headers; + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java index 65f5fc95..586702c2 100644 --- a/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/config/WebSecurityConfigIntegrationTest.java @@ -22,7 +22,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@TestPropertySource(properties = {"management.port=0", "server.port=0", "hsadminng.cas.server-url=http://localhost:8088/cas"}) +@TestPropertySource(properties = {"management.port=0", "server.port=0"}) @ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile! class WebSecurityConfigIntegrationTest { @@ -32,6 +32,9 @@ class WebSecurityConfigIntegrationTest { @Value("${local.management.port}") private int managementPort; + @Value("${hsadminng.cas.service}") + private String serviceUrl; + @Autowired private TestRestTemplate restTemplate; @@ -41,7 +44,7 @@ class WebSecurityConfigIntegrationTest { @Test public void shouldSupportPingEndpoint() { // given - wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=http://localhost:8080/api&ticket=test-user")) + wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=test-user")) .willReturn(aResponse() .withStatus(200) .withBody(""" diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 35ec34bf..a69f8aa1 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -53,5 +53,5 @@ testcontainers: hsadminng: cas: - server-url: fake - service-url: http://localhost:8080/api # not really used in test config + server: http://localhost:8088/cas # mocked via WireMock + service: http://localhost:8080/api # must match service used in WireMock mock response -- 2.39.5 From 6c38a328f3ad7a411dd0ee8fb7795f044eca5d1c Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 21 Dec 2024 15:17:20 +0100 Subject: [PATCH 14/24] disable CSRF --- .../net/hostsharing/hsadminng/config/WebSecurityConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java b/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java index 6c91ce17..d279ae12 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java +++ b/src/main/java/net/hostsharing/hsadminng/config/WebSecurityConfig.java @@ -5,6 +5,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; @Configuration @@ -22,6 +23,7 @@ public class WebSecurityConfig { .requestMatchers("/actuator/**").permitAll() .anyRequest().authenticated() ) + .csrf(AbstractHttpConfigurer::disable) .build(); } -- 2.39.5 From 3b9f48cfd6abf68c783aaff8ee91f77919d94e53 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 21 Dec 2024 15:30:16 +0100 Subject: [PATCH 15/24] add hsadmin-ng culr wrapper with CAS --- bin/hsadmin-ng | 140 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100755 bin/hsadmin-ng diff --git a/bin/hsadmin-ng b/bin/hsadmin-ng new file mode 100755 index 00000000..40a0dc4e --- /dev/null +++ b/bin/hsadmin-ng @@ -0,0 +1,140 @@ +#!/bin/bash + +if [ "$#" -eq 0 ]; then + cat <> [parameters] + + commands: +EOF + grep '") ''# ' $0 + exit +fi + +if [ "$1" == "--trace" ]; then + function trace() { + echo "$*" >&2 + } + function doCurl() { + set -x + curl "$@" + set +x + } + shift +else + function trace() { + : + } + function doCurl() { + curl "$@" + } +fi + +if [ -z "$HSADMINNG_CAS_LOGIN" ] || [ -z "$HSADMINNG_CAS_VALIDATE" ] || \ + [ -z "$HSADMINNG_CAS_USERNAME" ] || [ -z "$HSADMINNG_CAS_PASSWORD" ] || \ + [ -z "$HSADMINNG_CAS_SERVICE" ]; then + cat >&2 <> + export HSADMINNG_CAS_PASSWORD=<> + export HSADMINNG_CAS_SERVICE=https://hsadminng.hostsharing.net:443/ +EOF + exit 1 +fi + +function casLogin() { + HSADMINNG_CAS_TGT=`doCurl -s -i -X POST \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -d "username=$HSADMINNG_CAS_USERNAME&password=$HSADMINNG_CAS_PASSWORD" \ + $HSADMINNG_CAS_LOGIN -o /dev/null -D - \ + | grep -i "^Location: " | sed -e 's/^Location: //' -e 's/\\r//'` + echo "$HSADMINNG_CAS_TGT" >~/.cas-login-tgt + trace "$HSADMINNG_CAS_TGT" +} + +function casTicket() { + HSADMINNG_CAS_TGT=$(<~/.cas-login-tgt) + if [[ -z "$HSADMINNG_CAS_TGT" ]]; then + echo "ERROR: cannot get CAS ticket granting ticket for $HSADMINNG_CAS_USERNAME" >&2 + exit 1 + fi + trace "CAS-TGT: $HSADMINNG_CAS_TGT" + + trace "fetching CAS service ticket" + trace "curl -s -d \"service=$HSADMINNG_CAS_SERVICE\" $HSADMINNG_CAS_TGT" + HSADMINNG_CAS_TICKET=$(curl -s -d "service=$HSADMINNG_CAS_SERVICE" $HSADMINNG_CAS_TGT) + if [[ -z "$HSADMINNG_CAS_TICKET" ]]; then + echo "ERROR: cannot get CAS service ticket" >&2 + exit 1 + fi + + echo $HSADMINNG_CAS_TICKET +} + +function casValidate() { + HSADMINNG_CAS_TICKET=`casTicket` + + trace "validating CAS-TICKET: $HSADMINNG_CAS_TICKET" + trace curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE} + HSADMINNG_CAS_USER=`curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE} | grep -oPm1 "(?<=)[^<]+"` + if [ -z "$HSADMINNG_CAS_USER" ]; then + echo "validation failed" >&2 + exit 1 + fi + echo "CAS-User: $HSADMINNG_CAS_USER" +} + +if ! find ~/.cas-login-tgt -type f -size +0c -mmin -60 2>/dev/null | grep -q .; then + casLogin +fi + +case "$1" in + "login") # explicitly login using CAS-server and credentials in HSADMINNG_CAS_..., fetches ticket granting ticket + casLogin + ;; + "logout") # logout, deleting ticket granting ticket + rm ~/.cas-login-tgt + ;; + "validate") # validate user login and print currently logged in user + casValidate + ;; + "get") # HTTP GET, add URL as parameter + shift + HSADMINNG_CAS_TICKET=`casTicket` + #trace "curl -f -s --header \"Authorization: $HSADMINNG_CAS_TICKET\" " "$@" + doCurl -f -H "Authorization: $HSADMINNG_CAS_TICKET" "$*" + ;; + "post") # HTTP POST, add curl options to specify the request body and the URL as last parameter + shift + HSADMINNG_CAS_TICKET=`casTicket` + trace "curl -f --header \"Authorization: $HSADMINNG_CAS_TICKET\" --header \"Content-Type: application/json\" -X POST " "$@" + curl -f -H "Authorization: $HSADMINNG_CAS_TICKET" --header "Content-Type: application/json" -X POST "$@" + ;; + "patch") # HTTP PATCH, add curl options to specify the request body and the URL as last parameter + shift + HSADMINNG_CAS_TICKET=`casTicket` + trace "curl -f --header \"Authorization: $HSADMINNG_CAS_TICKET\" --header \"Content-Type: application/json\" -X PATCH " "$@" + curl -f -H "Authorization: $HSADMINNG_CAS_TICKET" --header "Content-Type: application/json" -X POST "$*" + ;; + "delete") # HTTP DELETE, add curl options to specify the request body and the URL as last parameter + shift + HSADMINNG_CAS_TICKET=`casTicket` + trace "curl -f --header \"Authorization: $HSADMINNG_CAS_TICKET\" -X DELETE " "$@" + curl -f -H "Authorization: $HSADMINNG_CAS_TICKET" -X POST "$@" + ;; + *) + cat >&2 < Date: Sat, 21 Dec 2024 15:51:17 +0100 Subject: [PATCH 16/24] read HSADMINNG_CAS_USERNAME + HSADMINNG_CAS_PASSWORD if missing in env --- bin/hsadmin-ng | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bin/hsadmin-ng b/bin/hsadmin-ng index 40a0dc4e..50f88b8f 100755 --- a/bin/hsadmin-ng +++ b/bin/hsadmin-ng @@ -31,7 +31,6 @@ else fi if [ -z "$HSADMINNG_CAS_LOGIN" ] || [ -z "$HSADMINNG_CAS_VALIDATE" ] || \ - [ -z "$HSADMINNG_CAS_USERNAME" ] || [ -z "$HSADMINNG_CAS_PASSWORD" ] || \ [ -z "$HSADMINNG_CAS_SERVICE" ]; then cat >&2 <> - export HSADMINNG_CAS_PASSWORD=<> + export HSADMINNG_CAS_USERNAME=<> export HSADMINNG_CAS_SERVICE=https://hsadminng.hostsharing.net:443/ EOF exit 1 fi function casLogin() { + + if [ -z "$HSADMINNG_CAS_USERNAME" ]; then + read -p "Username: " HSADMINNG_CAS_USERNAME + fi + + if [ -z "$HSADMINNG_CAS_PASSWORD" ]; then + read -p "Password: " HSADMINNG_CAS_PASSWORD + fi + HSADMINNG_CAS_TGT=`doCurl -s -i -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "username=$HSADMINNG_CAS_USERNAME&password=$HSADMINNG_CAS_PASSWORD" \ -- 2.39.5 From 1bfa404f92cc5a83a1a5022fc61b67c49e492fe6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 21 Dec 2024 15:54:32 +0100 Subject: [PATCH 17/24] improved help --- bin/hsadmin-ng | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/hsadmin-ng b/bin/hsadmin-ng index 50f88b8f..1857d89c 100755 --- a/bin/hsadmin-ng +++ b/bin/hsadmin-ng @@ -1,6 +1,6 @@ #!/bin/bash -if [ "$#" -eq 0 ]; then +if [ "$#" -eq 0 ] || [ "$1" == "help" ] || [ "$1" == "--help" ] || [ "$1" == "-h" ]; then cat <> [parameters] @@ -137,7 +137,7 @@ case "$1" in *) cat >&2 < Date: Sat, 21 Dec 2024 16:04:40 +0100 Subject: [PATCH 18/24] remove hardcoded CAS user and add --fail-with-body to curl --- bin/hsadmin-ng | 16 ++++++---------- .../hsadminng/config/CasAuthenticator.java | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/bin/hsadmin-ng b/bin/hsadmin-ng index 1857d89c..3dca47d8 100755 --- a/bin/hsadmin-ng +++ b/bin/hsadmin-ng @@ -17,7 +17,7 @@ if [ "$1" == "--trace" ]; then } function doCurl() { set -x - curl "$@" + curl --fail-with-body --header "Authorization: $HSADMINNG_CAS_TICKET" "$@" set +x } shift @@ -26,7 +26,7 @@ else : } function doCurl() { - curl "$@" + curl --fail-with-body --header "Authorization: $HSADMINNG_CAS_TICKET" "$@" } fi @@ -113,26 +113,22 @@ case "$1" in "get") # HTTP GET, add URL as parameter shift HSADMINNG_CAS_TICKET=`casTicket` - #trace "curl -f -s --header \"Authorization: $HSADMINNG_CAS_TICKET\" " "$@" - doCurl -f -H "Authorization: $HSADMINNG_CAS_TICKET" "$*" + doCurl "$*" ;; "post") # HTTP POST, add curl options to specify the request body and the URL as last parameter shift HSADMINNG_CAS_TICKET=`casTicket` - trace "curl -f --header \"Authorization: $HSADMINNG_CAS_TICKET\" --header \"Content-Type: application/json\" -X POST " "$@" - curl -f -H "Authorization: $HSADMINNG_CAS_TICKET" --header "Content-Type: application/json" -X POST "$@" + doCurl --header "Content-Type: application/json" -X POST "$@" ;; "patch") # HTTP PATCH, add curl options to specify the request body and the URL as last parameter shift HSADMINNG_CAS_TICKET=`casTicket` - trace "curl -f --header \"Authorization: $HSADMINNG_CAS_TICKET\" --header \"Content-Type: application/json\" -X PATCH " "$@" - curl -f -H "Authorization: $HSADMINNG_CAS_TICKET" --header "Content-Type: application/json" -X POST "$*" + doCurl --header "Content-Type: application/json" -X POST "$*" ;; "delete") # HTTP DELETE, add curl options to specify the request body and the URL as last parameter shift HSADMINNG_CAS_TICKET=`casTicket` - trace "curl -f --header \"Authorization: $HSADMINNG_CAS_TICKET\" -X DELETE " "$@" - curl -f -H "Authorization: $HSADMINNG_CAS_TICKET" -X POST "$@" + curl -X POST "$@" ;; *) cat >&2 < Date: Sat, 21 Dec 2024 16:23:13 +0100 Subject: [PATCH 19/24] rename hsadmin-ng to cas-curl --- .aliases | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.aliases b/.aliases index 5f200e79..cfd7c284 100644 --- a/.aliases +++ b/.aliases @@ -93,7 +93,7 @@ alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResource alias gw-test='. .aliases; ./gradlew test' alias gw-check='. .aliases; gw test check -x pitest' -alias hsadmin-ng='bin/hsadmin-ng' +alias cas-curl='bin/cas-curl' # etc/docker-compose.yml limits CPUs+MEM and includes a PostgreSQL config for analysing slow queries alias gw-importOfficeData-in-docker-compose=' diff --git a/README.md b/README.md index 1ca8e62e..cfa7f45f 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Next, compile and run the application without CAS-authentication on `localhost:8 export HSADMINNG_CAS_SERVER= gw bootRun -For using the REST-API with CAS-authentication, see `bin/hsadmin-ng`. +For using the REST-API with CAS-authentication, see `bin/cas-curl`. Now we can access the REST API, e.g. using curl: -- 2.39.5 From 88952bef7dc5bb3e72d25257a593cefe17d8ea4a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 21 Dec 2024 18:33:01 +0100 Subject: [PATCH 20/24] add @Timed("app.cas.authenticate") --- .../java/net/hostsharing/hsadminng/config/CasAuthenticator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java index 9fe55f5e..e0d8a9f1 100644 --- a/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java +++ b/src/main/java/net/hostsharing/hsadminng/config/CasAuthenticator.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.config; +import io.micrometer.core.annotation.Timed; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; @@ -25,6 +26,7 @@ public class CasAuthenticator implements Authenticator { private final RestTemplate restTemplate = new RestTemplate(); @SneakyThrows + @Timed("app.cas.authenticate") public String authenticate(final HttpServletRequest httpRequest) { final var userName = StringUtils.isBlank(casServerUrl) ? bypassCurrentSubject(httpRequest) -- 2.39.5 From 574fb92b9af0aca0514d5ab6a0f37fa31121a9ee Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 23 Dec 2024 09:36:44 +0100 Subject: [PATCH 21/24] HSADMINNG_CAS_SERVICE_ID + hsadmin-ng commands case insensitive --- bin/hsadmin-ng | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/hsadmin-ng b/bin/hsadmin-ng index 3dca47d8..2522c564 100755 --- a/bin/hsadmin-ng +++ b/bin/hsadmin-ng @@ -2,7 +2,7 @@ if [ "$#" -eq 0 ] || [ "$1" == "help" ] || [ "$1" == "--help" ] || [ "$1" == "-h" ]; then cat <> [parameters] commands: @@ -31,7 +31,7 @@ else fi if [ -z "$HSADMINNG_CAS_LOGIN" ] || [ -z "$HSADMINNG_CAS_VALIDATE" ] || \ - [ -z "$HSADMINNG_CAS_SERVICE" ]; then + [ -z "$HSADMINNG_CAS_SERVICE_ID" ]; then cat >&2 <> - export HSADMINNG_CAS_SERVICE=https://hsadminng.hostsharing.net:443/ + export HSADMINNG_CAS_SERVICE_ID=https://hsadminng.hostsharing.net:443/ EOF exit 1 fi @@ -73,8 +73,8 @@ function casTicket() { trace "CAS-TGT: $HSADMINNG_CAS_TGT" trace "fetching CAS service ticket" - trace "curl -s -d \"service=$HSADMINNG_CAS_SERVICE\" $HSADMINNG_CAS_TGT" - HSADMINNG_CAS_TICKET=$(curl -s -d "service=$HSADMINNG_CAS_SERVICE" $HSADMINNG_CAS_TGT) + trace "curl -s -d \"service=$HSADMINNG_CAS_SERVICE_ID\" $HSADMINNG_CAS_TGT" + HSADMINNG_CAS_TICKET=$(curl -s -d "service=$HSADMINNG_CAS_SERVICE_ID" $HSADMINNG_CAS_TGT) if [[ -z "$HSADMINNG_CAS_TICKET" ]]; then echo "ERROR: cannot get CAS service ticket" >&2 exit 1 @@ -87,8 +87,8 @@ function casValidate() { HSADMINNG_CAS_TICKET=`casTicket` trace "validating CAS-TICKET: $HSADMINNG_CAS_TICKET" - trace curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE} - HSADMINNG_CAS_USER=`curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE} | grep -oPm1 "(?<=)[^<]+"` + trace curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE_ID} + HSADMINNG_CAS_USER=`curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE_ID} | grep -oPm1 "(?<=)[^<]+"` if [ -z "$HSADMINNG_CAS_USER" ]; then echo "validation failed" >&2 exit 1 @@ -100,7 +100,7 @@ if ! find ~/.cas-login-tgt -type f -size +0c -mmin -60 2>/dev/null | grep -q .; casLogin fi -case "$1" in +case "${1,,}" in "login") # explicitly login using CAS-server and credentials in HSADMINNG_CAS_..., fetches ticket granting ticket casLogin ;; -- 2.39.5 From fcb003ac996e2bf1f9afb60a712194dc4092f4bc Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 23 Dec 2024 09:41:50 +0100 Subject: [PATCH 22/24] do not echo password --- bin/hsadmin-ng | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/hsadmin-ng b/bin/hsadmin-ng index 2522c564..2e310972 100755 --- a/bin/hsadmin-ng +++ b/bin/hsadmin-ng @@ -52,7 +52,7 @@ function casLogin() { fi if [ -z "$HSADMINNG_CAS_PASSWORD" ]; then - read -p "Password: " HSADMINNG_CAS_PASSWORD + read -s -p "Password: " HSADMINNG_CAS_PASSWORD fi HSADMINNG_CAS_TGT=`doCurl -s -i -X POST \ -- 2.39.5 From ab8e99cd9051f93597badc18094548e5200eb016 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 23 Dec 2024 09:46:23 +0100 Subject: [PATCH 23/24] rename hsadmin-ng -> cas-curl --- bin/{hsadmin-ng => cas-curl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/{hsadmin-ng => cas-curl} (100%) diff --git a/bin/hsadmin-ng b/bin/cas-curl similarity index 100% rename from bin/hsadmin-ng rename to bin/cas-curl -- 2.39.5 From 01e4929f8b98634311af7053a6c31dfc633e4b93 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 23 Dec 2024 12:47:45 +0100 Subject: [PATCH 24/24] fix visible password in cas-curl --trace --- bin/cas-curl | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/bin/cas-curl b/bin/cas-curl index 2e310972..41427a41 100755 --- a/bin/cas-curl +++ b/bin/cas-curl @@ -23,7 +23,7 @@ if [ "$1" == "--trace" ]; then shift else function trace() { - : + : # noop } function doCurl() { curl --fail-with-body --header "Authorization: $HSADMINNG_CAS_TICKET" "$@" @@ -45,23 +45,40 @@ EOF exit 1 fi +function casLogout() { + rm -f ~/.cas-login-tgt +} + function casLogin() { + # ticket granting ticket exists and not expired? + if find ~/.cas-login-tgt -type f -size +0c -mmin -60 2>/dev/null | grep -q .; then + return + fi if [ -z "$HSADMINNG_CAS_USERNAME" ]; then - read -p "Username: " HSADMINNG_CAS_USERNAME + read -e -p "Username: " HSADMINNG_CAS_USERNAME fi if [ -z "$HSADMINNG_CAS_PASSWORD" ]; then - read -s -p "Password: " HSADMINNG_CAS_PASSWORD + read -s -e -p "Password: " HSADMINNG_CAS_PASSWORD fi - HSADMINNG_CAS_TGT=`doCurl -s -i -X POST \ + # Do NOT use doCurl here! We do neither want to print the password nor pass a CAS service ticket. + trace "+ curl --fail-with-body -s -i -X POST \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -d \"username=$HSADMINNG_CAS_USERNAME&password=<>\" \ + $HSADMINNG_CAS_LOGIN -o ~/.cas-login-tgt.response -D -" + HSADMINNG_CAS_TGT=`curl --fail-with-body -s -i -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "username=$HSADMINNG_CAS_USERNAME&password=$HSADMINNG_CAS_PASSWORD" \ - $HSADMINNG_CAS_LOGIN -o /dev/null -D - \ + $HSADMINNG_CAS_LOGIN -o ~/.cas-login-tgt.response -D - \ | grep -i "^Location: " | sed -e 's/^Location: //' -e 's/\\r//'` - echo "$HSADMINNG_CAS_TGT" >~/.cas-login-tgt - trace "$HSADMINNG_CAS_TGT" + if [ -z "$HSADMINNG_CAS_TGT" ]; then + echo "ERROR: could not get ticket granting ticket" >&2 + cat ~/.cas-login-tgt.response >&2 + fi + echo "$HSADMINNG_CAS_TGT" >~/.cas-login-tgt + trace "$HSADMINNG_CAS_TGT" } function casTicket() { @@ -87,6 +104,7 @@ function casValidate() { HSADMINNG_CAS_TICKET=`casTicket` trace "validating CAS-TICKET: $HSADMINNG_CAS_TICKET" + # Do NOT use doCurl here! We do not pass a CAS service ticket. trace curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE_ID} HSADMINNG_CAS_USER=`curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE_ID} | grep -oPm1 "(?<=)[^<]+"` if [ -z "$HSADMINNG_CAS_USER" ]; then @@ -96,37 +114,40 @@ function casValidate() { echo "CAS-User: $HSADMINNG_CAS_USER" } -if ! find ~/.cas-login-tgt -type f -size +0c -mmin -60 2>/dev/null | grep -q .; then - casLogin -fi - case "${1,,}" in - "login") # explicitly login using CAS-server and credentials in HSADMINNG_CAS_..., fetches ticket granting ticket + "login") # reads username+password and fetches ticket granting ticket (bypasses HSADMINNG_CAS_USERNAME+HSADMINNG_CAS_PASSWORD) + casLogout + export HSADMINNG_CAS_USERNAME= + export HSADMINNG_CAS_PASSWORD= casLogin ;; "logout") # logout, deleting ticket granting ticket - rm ~/.cas-login-tgt + casLogout ;; - "validate") # validate user login and print currently logged in user + "validate") # validates ticket granting ticket and prints currently logged in user casValidate ;; "get") # HTTP GET, add URL as parameter shift + casLogin HSADMINNG_CAS_TICKET=`casTicket` doCurl "$*" ;; "post") # HTTP POST, add curl options to specify the request body and the URL as last parameter shift + casLogin HSADMINNG_CAS_TICKET=`casTicket` doCurl --header "Content-Type: application/json" -X POST "$@" ;; "patch") # HTTP PATCH, add curl options to specify the request body and the URL as last parameter shift + casLogin HSADMINNG_CAS_TICKET=`casTicket` doCurl --header "Content-Type: application/json" -X POST "$*" ;; "delete") # HTTP DELETE, add curl options to specify the request body and the URL as last parameter shift + casLogin HSADMINNG_CAS_TICKET=`casTicket` curl -X POST "$@" ;; -- 2.39.5