diff --git a/.github/workflows/dependency-check-maven.yml b/.github/workflows/dependency-check-maven.yml
index 997b347..50a02df 100644
--- a/.github/workflows/dependency-check-maven.yml
+++ b/.github/workflows/dependency-check-maven.yml
@@ -6,20 +6,20 @@ on:
- cron: "0 10 * * 6"
jobs:
-
dependency-check-maven:
-
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
ref: develop
+
- name: Set up JDK 21
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
+ distribution: temurin
java-version: '21'
- distribution: 'adopt'
cache: maven
+
- name: Execute OWASP dependency check
run: mvn clean install -P security-updates -DskipTests=true -B
diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml
index c722812..b4da8d6 100644
--- a/.github/workflows/develop.yml
+++ b/.github/workflows/develop.yml
@@ -11,7 +11,6 @@ on:
jobs:
build:
-
runs-on: ubuntu-latest
steps:
@@ -19,8 +18,8 @@ jobs:
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
+ distribution: temurin
java-version: '21'
- distribution: 'adopt'
cache: maven
- name: Fail build if version contains SNAPSHOT
run: |
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 82414e1..957b682 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -17,8 +17,8 @@ jobs:
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
+ distribution: temurin
java-version: '21'
- distribution: 'adopt'
cache: maven
- run: echo "VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
- run: echo "BUILD_VERSION=${VERSION%-SNAPSHOT}" >> $GITHUB_ENV
diff --git a/oc-sso-notificatie/pom.xml b/oc-sso-notificatie/pom.xml
index 09b4c39..9a7aa62 100644
--- a/oc-sso-notificatie/pom.xml
+++ b/oc-sso-notificatie/pom.xml
@@ -19,7 +19,7 @@
oc-sso-notificatie-parent
nl.kennisnet.services
- 2.4.1-SNAPSHOT
+ 2.5.0-SNAPSHOT
4.0.0
diff --git a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/Application.java b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/Application.java
index 2247a6a..6db861c 100644
--- a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/Application.java
+++ b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/Application.java
@@ -18,10 +18,11 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
-import org.springframework.web.client.RestTemplate;
+import org.springframework.http.client.JdkClientHttpRequestFactory;
+import org.springframework.web.client.RestClient;
+import java.net.http.HttpClient;
import java.time.Duration;
/**
@@ -38,11 +39,13 @@ public static void main(String[] args) {
}
@Bean
- public RestTemplate restTemplate(RestTemplateBuilder builder) {
- return builder
- .connectTimeout(Duration.ofSeconds(connectionTimeout))
- .readTimeout(Duration.ofSeconds(connectionTimeout))
- .build();
+ public RestClient restClient() {
+ HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(connectionTimeout)).build();
+
+ JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory(httpClient);
+ requestFactory.setReadTimeout(Duration.ofSeconds(connectionTimeout));
+
+ return RestClient.builder().requestFactory(requestFactory).build();
}
}
diff --git a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/config/BuildPropertiesConfig.java b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/config/BuildPropertiesConfig.java
new file mode 100644
index 0000000..50c8e85
--- /dev/null
+++ b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/config/BuildPropertiesConfig.java
@@ -0,0 +1,55 @@
+package nl.kennisnet.services.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.PropertiesLoaderUtils;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Properties;
+
+@Configuration
+public class BuildPropertiesConfig {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BuildPropertiesConfig.class);
+
+ @Bean
+ public BuildProperties buildProperties(ResourceLoader resourceLoader) {
+ try {
+ Resource resource = resourceLoader.getResource("classpath:META-INF/build-info.properties");
+ if (!resource.exists()) {
+ LOGGER.warn("META-INF/build-info.properties not found, using default build properties");
+ Properties defaultProperties = new Properties();
+ defaultProperties.put("time", Instant.now().toString());
+ defaultProperties.put("version", "0.0.0-TEST");
+ defaultProperties.put("name", "test-build");
+ defaultProperties.put("group", "test-group");
+ defaultProperties.put("artifact", "test-artifact");
+ return new BuildProperties(defaultProperties);
+ }
+
+ Properties properties = PropertiesLoaderUtils.loadProperties(resource);
+ Properties flattenedProperties = new Properties();
+ properties.forEach((key, value) -> {
+ String newKey = key.toString().replaceFirst("^build\\.", "");
+ flattenedProperties.put(newKey, value);
+ });
+
+ return new BuildProperties(flattenedProperties);
+ } catch (IOException e) {
+ LOGGER.error("Error loading build-info.properties", e);
+ Properties defaultProperties = new Properties();
+ defaultProperties.put("time", Instant.now().toString());
+ defaultProperties.put("version", "0.0.0-ERROR");
+ defaultProperties.put("name", "error-build");
+ defaultProperties.put("group", "error-group");
+ defaultProperties.put("artifact", "error-artifact");
+ return new BuildProperties(defaultProperties);
+ }
+ }
+}
diff --git a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/config/CookieSameSiteConfiguration.java b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/config/CookieSameSiteConfiguration.java
index 763e949..49a2af2 100644
--- a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/config/CookieSameSiteConfiguration.java
+++ b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/config/CookieSameSiteConfiguration.java
@@ -15,7 +15,7 @@
*/
package nl.kennisnet.services.web.config;
-import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
+import org.springframework.boot.web.server.servlet.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
diff --git a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/CacheHashService.java b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/CacheHashService.java
index 5b75f3f..ceef5a9 100644
--- a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/CacheHashService.java
+++ b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/CacheHashService.java
@@ -3,15 +3,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;
+import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
@Service
public class CacheHashService {
@@ -27,10 +24,10 @@ public class CacheHashService {
@Value("${api.endpoint.url.cacheHash:#{null}}")
private String url;
- private final RestTemplate restTemplate;
+ private final RestClient restClient;
- public CacheHashService(RestTemplate restTemplate) {
- this.restTemplate = restTemplate;
+ public CacheHashService(RestClient restClient) {
+ this.restClient = restClient;
}
public String fetchCacheHash() {
@@ -45,10 +42,12 @@ public String fetchCacheHash() {
HttpEntity> httpEntity = new HttpEntity<>(requestHeaders);
try {
- ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, httpEntity,
- new ParameterizedTypeReference<>() {});
+ String result = restClient.get()
+ .uri(url)
+ .headers(headers -> headers.addAll(httpEntity.getHeaders()))
+ .retrieve()
+ .body(String.class);
- String result = response.getBody();
if (null == result) {
LOGGER.warn("Received null from data-services cache-hash, returning empty hash");
return "";
diff --git a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/IdPProvider.java b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/IdPProvider.java
index 95ca297..db53cb9 100644
--- a/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/IdPProvider.java
+++ b/oc-sso-notificatie/src/main/java/nl/kennisnet/services/web/service/IdPProvider.java
@@ -27,12 +27,11 @@
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.IOException;
@@ -52,7 +51,7 @@ public class IdPProvider {
private static final ObjectMapper objectMapper = new ObjectMapper();
- private final RestTemplate restTemplate;
+ private final RestClient restClient;
@Value("${data.location}")
private Resource dataSource;
@@ -69,8 +68,8 @@ public class IdPProvider {
@Value("${api.endpoint.url.all-suffix:#{null}}")
private String endpointAllSuffix;
- public IdPProvider(RestTemplate restTemplate) {
- this.restTemplate = restTemplate;
+ public IdPProvider(RestClient restClient) {
+ this.restClient = restClient;
}
/**
@@ -102,8 +101,11 @@ private List getAllSsoNotificationsFromDataServices() {
URI uri = UriComponentsBuilder.fromUriString(endpointUrl + endpointAllSuffix).build().toUri();
try {
- return restTemplate.exchange(uri, HttpMethod.GET, httpEntity,
- new ParameterizedTypeReference>(){}).getBody();
+ return restClient.get()
+ .uri(uri)
+ .headers(headers -> headers.addAll(httpEntity.getHeaders()))
+ .retrieve()
+ .body(new ParameterizedTypeReference>() {});
} catch (HttpClientErrorException hcee) {
if (HttpStatus.NOT_FOUND == hcee.getStatusCode()) {
return new ArrayList<>();
diff --git a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/CacheHashServiceTest.java b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/CacheHashServiceTest.java
index 59a5ae5..27defb3 100644
--- a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/CacheHashServiceTest.java
+++ b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/CacheHashServiceTest.java
@@ -3,87 +3,91 @@
import nl.kennisnet.services.web.service.CacheHashService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.mockito.InjectMocks;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
-@SpringBootTest
+@ExtendWith(MockitoExtension.class)
class CacheHashServiceTest {
@Mock
- RestTemplate restTemplate;
+ RestClient restClient;
- @InjectMocks
- CacheHashService cacheHashService;
+ private CacheHashService cacheHashService;
@BeforeEach
void setUp() {
+ cacheHashService = new CacheHashService(restClient);
+
ReflectionTestUtils.setField(cacheHashService, "url", "sso-notification-url");
ReflectionTestUtils.setField(cacheHashService, "API_KEY_HEADER", "api-key");
}
@Test
void fetchCacheHashTest() {
- when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class),
- any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>("HASH", HttpStatus.OK));
+ RestClient.RequestHeadersUriSpec> spec = buildRequestSpec("HASH");
- String result = cacheHashService.fetchCacheHash();
+ when(restClient.get()).thenReturn((RestClient.RequestHeadersUriSpec) spec);
- assertEquals("HASH", result);
+ assertEquals("HASH", cacheHashService.fetchCacheHash());
}
@Test
void fetchCacheNullReturnTest() {
- when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class),
- any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(null, HttpStatus.OK));
+ RestClient.RequestHeadersUriSpec> spec = buildRequestSpec(null);
- String result = cacheHashService.fetchCacheHash();
+ when(restClient.get()).thenReturn((RestClient.RequestHeadersUriSpec) spec);
- assertEquals("", result);
+ assertEquals("", cacheHashService.fetchCacheHash());
}
@Test
void fetchCacheNullUrlTest() {
ReflectionTestUtils.setField(cacheHashService, "url", null);
- when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class),
- any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(null, HttpStatus.OK));
-
- String result = cacheHashService.fetchCacheHash();
-
- assertEquals("", result);
+ assertEquals("", cacheHashService.fetchCacheHash());
}
@Test
void fetchCacheHashHttpExceptionTest() {
- when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class),
- any(ParameterizedTypeReference.class))).thenThrow(new HttpClientErrorException(HttpStatus.FORBIDDEN));
+ when(restClient.get()).thenThrow(new HttpClientErrorException(HttpStatus.FORBIDDEN));
- String result = cacheHashService.fetchCacheHash();
-
- assertEquals("", result);
+ assertEquals("", cacheHashService.fetchCacheHash());
}
@Test
void fetchCacheRestClientExceptionTest() {
- when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class),
- any(ParameterizedTypeReference.class))).thenThrow(new RestClientException("ERROR"));
+ when(restClient.get()).thenThrow(new RestClientException("ERROR"));
+
+ assertEquals("", cacheHashService.fetchCacheHash());
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private RestClient.RequestHeadersUriSpec> buildRequestSpec(String responseBody) {
+
+ RestClient.RequestHeadersUriSpec requestSpec =
+ mock(RestClient.RequestHeadersUriSpec.class);
+
+ RestClient.RequestHeadersSpec headersSpec =
+ mock(RestClient.RequestHeadersSpec.class);
+
+ RestClient.ResponseSpec responseSpec =
+ mock(RestClient.ResponseSpec.class);
- String result = cacheHashService.fetchCacheHash();
+ when(requestSpec.uri(anyString())).thenReturn(headersSpec);
+ when(headersSpec.headers(any())).thenReturn(headersSpec);
+ when(headersSpec.retrieve()).thenReturn(responseSpec);
+ when(responseSpec.body(String.class)).thenReturn(responseBody);
- assertEquals("", result);
+ return requestSpec;
}
}
\ No newline at end of file
diff --git a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/IdPProviderTest.java b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/IdPProviderTest.java
index 127daf3..d51c698 100644
--- a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/IdPProviderTest.java
+++ b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/IdPProviderTest.java
@@ -17,51 +17,53 @@
import nl.kennisnet.services.web.model.IdP;
import nl.kennisnet.services.web.service.IdPProvider;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.ReflectionTestUtils;
-import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.HttpClientErrorException;
-import org.springframework.web.util.UriComponentsBuilder;
+import org.springframework.web.client.RestClient;
-import java.net.URI;
-import java.nio.file.Files;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
-import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
-import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
+import static org.mockito.Mockito.when;
-@RestClientTest(IdPProvider.class)
-@TestPropertySource(properties = "classpath:application.properties")
+@ExtendWith(MockitoExtension.class)
class IdPProviderTest {
- @Autowired
- private IdPProvider provider;
-
- @Autowired
- private MockRestServiceServer server;
-
- @Value("classpath:rest_response.json")
- private Resource resourceFile;
+ @Mock
+ private RestClient restClient;
@Value("${api.endpoint.url}")
private String endpointUrl;
- @Test
- void getIdPTest() throws Exception {
- String resource = new String(Files.readAllBytes(resourceFile.getFile().toPath()));
- this.server.expect(requestTo(createUrl())).andRespond(withSuccess(resource, MediaType.APPLICATION_JSON));
+ private IdPProvider idPProvider;
+
+ private Resource restResource;
- List idps = provider.getAllSsoNotifications();
- IdP idp = idps.get(0);
+ private Resource staticResource;
+
+ @BeforeEach
+ void setUp() {
+ idPProvider = new IdPProvider(restClient);
+
+ staticResource = new ClassPathResource("idp.data.json");
+ restResource = new ClassPathResource("rest_response.json");
+
+ ReflectionTestUtils.setField(idPProvider, "dataSource", restResource);
+ }
+
+ @Test
+ void getIdPTest() {
+ List idps = idPProvider.getAllSsoNotifications();
+ IdP idp = idps.getFirst();
assertNotNull(idp);
assertEquals("RefELOSAML-OpenConext", idp.getEntityId());
assertNotNull(idp.getIdpUrlList());
@@ -75,27 +77,37 @@ void getIdPTest() throws Exception {
assertTrue(idp.getRedirectUrlList().contains("https://referentie.vm.openconext.org"));
assertTrue(idp.getRedirectUrlList().contains("https://referentie.vm2.openconext.org"));
assertTrue(idp.getRedirectUrlList().contains("https://referentie.vm3.openconext.org"));
- this.server.verify();
}
@Test
void invalidApiKeyTest() {
- this.server.expect(requestTo(createUrl())).andRespond(withUnauthorizedRequest());
- assertThrows(HttpClientErrorException.Unauthorized.class, () -> provider.getAllSsoNotifications());
+ ReflectionTestUtils.setField(idPProvider, "endpointUrl", "http://localhost:3000/api/sso-notification");
+ ReflectionTestUtils.setField(idPProvider, "apiKeyHeaderKey", "api-key");
+ ReflectionTestUtils.setField(idPProvider, "apiKeyHeaderValue", "TESTTOKEN");
+ ReflectionTestUtils.setField(idPProvider, "endpointAllSuffix", "/all");
+
+ when(restClient.get()).thenThrow(new HttpClientErrorException(HttpStatus.UNAUTHORIZED));
+ assertThrows(HttpClientErrorException.class, () -> idPProvider.getAllSsoNotifications());
}
@Test
- void notFoundResponseTest() {
- this.server.expect(requestTo(createUrl())).andRespond(withStatus(HttpStatus.NOT_FOUND));
- assertTrue(provider.getAllSsoNotifications().isEmpty());
+ void notFoundResponseTest() {
+ ReflectionTestUtils.setField(idPProvider, "endpointUrl", "http://localhost:3000/api/sso-notification");
+ ReflectionTestUtils.setField(idPProvider, "apiKeyHeaderKey", "api-key");
+ ReflectionTestUtils.setField(idPProvider, "apiKeyHeaderValue", "TESTTOKEN");
+ ReflectionTestUtils.setField(idPProvider, "endpointAllSuffix", "/all");
+
+ when(restClient.get()).thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND));
+
+ assertTrue(idPProvider.getAllSsoNotifications().isEmpty());
}
@Test
void getIdPStaticFileTest() {
- ReflectionTestUtils.setField(provider, "endpointUrl", null);
+ ReflectionTestUtils.setField(idPProvider, "dataSource", staticResource);
- List idps = provider.getAllSsoNotifications();
- IdP idp = idps.get(0);
+ List idps = idPProvider.getAllSsoNotifications();
+ IdP idp = idps.getFirst();
assertNotNull(idp);
assertEquals("xxx", idp.getEntityId());
assertNotNull(idp.getIdpUrlList());
@@ -109,14 +121,9 @@ void getIdPStaticFileTest() {
@Test
void getIdpInvalidResourceStaticFileTest() {
- ReflectionTestUtils.setField(provider, "dataSource", new ClassPathResource("file_does_not_exist"));
- List idps = provider.getAllSsoNotifications();
+ ReflectionTestUtils.setField(idPProvider, "dataSource", new ClassPathResource("file_does_not_exist"));
+ List idps = idPProvider.getAllSsoNotifications();
assertTrue(idps.isEmpty());
}
- private URI createUrl() {
- String ALL_SUFFIX = "/all";
- return UriComponentsBuilder.fromUriString(endpointUrl + ALL_SUFFIX).build().toUri();
- }
-
}
diff --git a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/config/BuildPropertiesConfigTest.java b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/config/BuildPropertiesConfigTest.java
new file mode 100644
index 0000000..6e49e2a
--- /dev/null
+++ b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/config/BuildPropertiesConfigTest.java
@@ -0,0 +1,91 @@
+package nl.kennisnet.services.web.config;
+
+import nl.kennisnet.services.config.BuildPropertiesConfig;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.time.Instant;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+public class BuildPropertiesConfigTest {
+
+ @Mock
+ private ResourceLoader resourceLoader;
+
+ @InjectMocks
+ private final BuildPropertiesConfig buildPropertiesConfig = new BuildPropertiesConfig();
+
+
+ @Test
+ void buildPropertiesMissing() {
+ Resource mockResource = mock(Resource.class);
+ when(mockResource.exists()).thenReturn(false);
+ when(resourceLoader.getResource(any())).thenReturn(mockResource);
+
+ BuildProperties buildProperties = buildPropertiesConfig.buildProperties(resourceLoader);
+
+ assertEquals("0.0.0-TEST", buildProperties.getVersion());
+ assertEquals("test-build", buildProperties.getName());
+ assertEquals("test-group", buildProperties.getGroup());
+ assertEquals("test-artifact", buildProperties.getArtifact());
+ }
+
+ @Test
+ void buildPropertiesAvailable() throws IOException {
+ Resource mockResource = mock(Resource.class);
+
+ when(mockResource.exists()).thenReturn(true);
+ when(mockResource.getInputStream()).thenReturn(
+ new ByteArrayInputStream((
+ """
+ build.artifact=oc-sso-notificatie
+ build.group=nl.kennisnet.services
+ build.java_version=21
+ build.name=OC SSO Notification - Application
+ build.spring_boot_version=4.0.5
+ build.time=2026-04-13T10\\:35\\:03.571Z
+ build.version=2.5.0""").getBytes()));
+
+ when(resourceLoader.getResource(any())).thenReturn(mockResource);
+
+ BuildProperties buildProperties = buildPropertiesConfig.buildProperties(resourceLoader);
+
+ assertEquals("oc-sso-notificatie", buildProperties.getArtifact());
+ assertEquals("nl.kennisnet.services", buildProperties.getGroup());
+ assertEquals("OC SSO Notification - Application", buildProperties.getName());
+ assertEquals("2.5.0", buildProperties.getVersion());
+ assertEquals(Instant.parse("2026-04-13T10:35:03.571Z"), buildProperties.getTime());
+ assertEquals("21", buildProperties.get("java_version"));
+ assertEquals("4.0.5", buildProperties.get("spring_boot_version"));
+ }
+
+ @Test
+ void buildPropertiesMissingIOException() throws IOException {
+ Resource mockResource = mock(Resource.class);
+
+ when(mockResource.exists()).thenReturn(true);
+ doThrow(IOException.class).when(mockResource).getInputStream();
+
+ when(resourceLoader.getResource(any())).thenReturn(mockResource);
+
+ BuildProperties buildProperties = buildPropertiesConfig.buildProperties(resourceLoader);
+
+ assertEquals("0.0.0-ERROR", buildProperties.getVersion());
+ assertEquals("error-build", buildProperties.getName());
+ assertEquals("error-group", buildProperties.getGroup());
+ assertEquals("error-artifact", buildProperties.getArtifact());
+ }
+
+}
diff --git a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/controller/SsoNotificationControllerTest.java b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/controller/SsoNotificationControllerTest.java
index 73f79ec..7d0fc80 100644
--- a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/controller/SsoNotificationControllerTest.java
+++ b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/controller/SsoNotificationControllerTest.java
@@ -24,9 +24,8 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
-import org.springframework.boot.test.context.SpringBootTest;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpHeaders;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
@@ -40,27 +39,33 @@
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-@ExtendWith(SpringExtension.class)
-@SpringBootTest
+@ExtendWith(MockitoExtension.class)
class SsoNotificationControllerTest {
private static final String SINGLE_URL = "singleUrl";
+
private static final String SINGLE_URL_WITH_PATH = "singleUrlWithPath";
+
private static final String INVALID = "invalid";
private static final String MULTIPLE_URLS = "multipleUrls";
+
private static final String NO_PATH_URL_IDP = "noIdpUrlConfigured";
+
private static final String NO_PATH_URL_REDIRECT = "noRedirectUrlConfigured";
private static final String SINGLE_WILDCARD_URL = "singleWildcardUrl";
private static final String SSO_NOTIFICATION_URL = "/";
+
private static final String URL = "url";
+
private static final String REDIRECT_URI = "redirectUri";
+
private MockMvc mvc;
@InjectMocks
- private SsoNotificationController controller;
+ private SsoNotificationController ssoNotificationController;
@Mock
private CacheConfig cacheConfig;
@@ -72,8 +77,8 @@ class SsoNotificationControllerTest {
private IdPProvider idPProvider;
@BeforeEach
- void setUp() throws Exception {
- this.mvc = MockMvcBuilders.standaloneSetup(controller).build();
+ void setUp() {
+ this.mvc = MockMvcBuilders.standaloneSetup(ssoNotificationController).build();
ArrayList ssoNotifications = new ArrayList<>();
ssoNotifications.add(new IdP(NO_PATH_URL_IDP, null, List.of("http://www.example.com")));
@@ -90,8 +95,6 @@ void setUp() throws Exception {
List.of("http://www.example.com")));
when(idPProvider.getAllSsoNotifications()).thenReturn(ssoNotifications);
- when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
- new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
}
@Test
@@ -148,6 +151,9 @@ void processSsoNotificationInvalidReferrerBasicAuthTest() throws Exception {
@Test
void processSsoNotificationNoRedirectUrlParamTest() throws Exception {
+ when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
+ new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
+
mvc.perform(MockMvcRequestBuilders.get(SSO_NOTIFICATION_URL).param("id", SINGLE_URL)
.param(URL, "http://www.example.com"))
.andExpect(status().isOk())
@@ -156,6 +162,9 @@ void processSsoNotificationNoRedirectUrlParamTest() throws Exception {
@Test
void processSsoNotificationRedirectTest() throws Exception {
+ when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
+ new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
+
mvc.perform(MockMvcRequestBuilders.get(SSO_NOTIFICATION_URL).param("id", "multipleUrls")
.param(URL, "http://www.example.com")
.param(REDIRECT_URI, "http://www.exampledomain.com"))
@@ -166,6 +175,9 @@ void processSsoNotificationRedirectTest() throws Exception {
@Test
void processSsoNotificationFromIframeTest() throws Exception {
+ when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
+ new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
+
mvc.perform(MockMvcRequestBuilders.get(SSO_NOTIFICATION_URL).param("id", "multipleUrls")
.param(URL, "http://www.example.com"))
.andExpect(status().isOk())
@@ -174,6 +186,9 @@ void processSsoNotificationFromIframeTest() throws Exception {
@Test
void processSsoNotificationFromIframeWithRedirectTest() throws Exception {
+ when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
+ new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
+
mvc.perform(MockMvcRequestBuilders.get(SSO_NOTIFICATION_URL).param("id", "multipleUrls")
.param(REDIRECT_URI, "http://www.example.com")
.param(URL, "http://www.example.com"))
@@ -184,6 +199,9 @@ void processSsoNotificationFromIframeWithRedirectTest() throws Exception {
@Test
void processSsoNotificationFromIframeWildcardTest() throws Exception {
+ when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
+ new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
+
mvc.perform(MockMvcRequestBuilders.get(SSO_NOTIFICATION_URL).param("id", SINGLE_WILDCARD_URL)
.param(URL, "http://test.example.com"))
.andExpect(cookie().exists(SsoNotificationController.COOKIE_NOTIFICATION))
@@ -229,6 +247,9 @@ void processSsoNotificationNoRedirectExpressionsTest() throws Exception {
@Test
void testUrlPathTest() throws Exception {
+ when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
+ new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
+
mvc.perform(MockMvcRequestBuilders.get(SSO_NOTIFICATION_URL).param("id", SINGLE_URL_WITH_PATH)
.header(HttpHeaders.REFERER, "http://www.example.com"))
.andExpect(status().isOk())
@@ -258,6 +279,9 @@ void processNotExistingId() throws Exception {
@Test
void testDataFetchError() throws Exception {
+ when(cookiesHandler.createCookie(anyString(), nullable(String.class), any(URL.class), any())).thenReturn(
+ new Cookie(SsoNotificationController.COOKIE_NOTIFICATION, "testValue"));
+
mvc.perform(MockMvcRequestBuilders.get(SSO_NOTIFICATION_URL).param("id", SINGLE_URL_WITH_PATH)
.param(REDIRECT_URI, "http://www.example.com")
.header(HttpHeaders.REFERER, "http://www.example.com"));
diff --git a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/jobs/DataServicesUpdateRunnerTest.java b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/jobs/DataServicesUpdateRunnerTest.java
index a98cd8c..6b3e923 100644
--- a/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/jobs/DataServicesUpdateRunnerTest.java
+++ b/oc-sso-notificatie/src/test/java/nl/kennisnet/services/web/jobs/DataServicesUpdateRunnerTest.java
@@ -5,14 +5,15 @@
import nl.kennisnet.services.web.service.jobs.DataServicesUpdateRunner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
-import org.springframework.boot.test.context.SpringBootTest;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import static org.mockito.Mockito.*;
-@SpringBootTest
+@ExtendWith(MockitoExtension.class)
class DataServicesUpdateRunnerTest {
@Mock
@@ -71,4 +72,4 @@ void canNotRunDouble() {
verify(cacheConfig, times(0)).cacheEvict();
}
-}
\ No newline at end of file
+}
diff --git a/pom.xml b/pom.xml
index cec77b1..c55f04e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
nl.kennisnet.services
oc-sso-notificatie-parent
pom
- 2.4.1-SNAPSHOT
+ 2.5.0-SNAPSHOT
oc-sso-notificatie
release
@@ -41,7 +41,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.10
+ 4.0.5
diff --git a/release/pom.xml b/release/pom.xml
index 3333029..aab751b 100644
--- a/release/pom.xml
+++ b/release/pom.xml
@@ -19,7 +19,7 @@
oc-sso-notificatie-parent
nl.kennisnet.services
- 2.4.1-SNAPSHOT
+ 2.5.0-SNAPSHOT
4.0.0
@@ -31,7 +31,7 @@
- 2.4.0,2.3.8,2.3.7,2.3.6,2.3.5,2.3.4,2.3.3,2.3.2,2.3.1,2.3.0,2.2.1,2.2.0,2.1.1,2.1.0,2.0.6,2.0.5,2.0.4,2.0.3,2.0.2,2.0.1,1.0.0
+ 2.5.0,2.4.0,2.3.8,2.3.7,2.3.6,2.3.5,2.3.4,2.3.3,2.3.2,2.3.1,2.3.0,2.2.1,2.2.0,2.1.1,2.1.0,2.0.6,2.0.5,2.0.4,2.0.3,2.0.2,2.0.1,1.0.0
diff --git a/release/src/site/markdown/docs/2.5.0/release-notes.md b/release/src/site/markdown/docs/2.5.0/release-notes.md
new file mode 100644
index 0000000..befe49f
--- /dev/null
+++ b/release/src/site/markdown/docs/2.5.0/release-notes.md
@@ -0,0 +1,21 @@
+# OC SSO Notification Service - Release Notes
+
+| Field | Value |
+|:--------------------------|:----------------------------|
+| Application | OC SSO Notification Service |
+| Version | 2.5.0 |
+| Changes since version | 2.4.0 |
+| Release date | 14-04-2026 (dd-mm-yyyy) |
+| Delivery type | Full release |
+
+## Changes
+
+| # | Description |
+|:---------|:----------------------------------------|
+| ENT-5402 | Build release OC SSO Notification 2.5.0 |
+
+## Configuration changes
+
+## Known vulnerabilities
+
+At the time of release, this version of the application had no known vulnerabilities with a CVE of 7 or higher.