From ed6b606958920e82b6e9448ebdfb8ba80c69be3e Mon Sep 17 00:00:00 2001 From: Dario Ghunney Ware Date: Thu, 7 Aug 2025 12:40:13 +0100 Subject: [PATCH] Cleanup --- .../src/main/resources/application.properties | 2 +- .../src/main/resources/settings.yml.template | 2 +- .../main/resources/static/js/fetch-utils.js | 10 -- .../src/main/resources/static/js/jwt-init.js | 53 +----- .../CustomAuthenticationSuccessHandler.java | 14 +- .../service/KeyPersistenceService.java | 8 +- .../KeyPersistenceServiceInterface.java | 1 + .../CustomLogoutSuccessHandlerTest.java | 10 +- .../JwtAuthenticationEntryPointTest.java | 24 ++- .../filter/JwtAuthenticationFilterTest.java | 129 ++++++++------ ...l2AuthenticationRequestRepositoryTest.java | 133 ++++++++------ .../security/service/JwtServiceTest.java | 163 +++++++++++------- .../KeyPersistenceServiceInterfaceTest.java | 105 +++++++---- 13 files changed, 349 insertions(+), 305 deletions(-) diff --git a/app/core/src/main/resources/application.properties b/app/core/src/main/resources/application.properties index c5a5fe0c3..0ca864985 100644 --- a/app/core/src/main/resources/application.properties +++ b/app/core/src/main/resources/application.properties @@ -50,4 +50,4 @@ spring.main.allow-bean-definition-overriding=true java.io.tmpdir=${stirling.tempfiles.directory:${java.io.tmpdir}/stirling-pdf} # V2 features -v2=true +v2=false diff --git a/app/core/src/main/resources/settings.yml.template b/app/core/src/main/resources/settings.yml.template index ac54de2a0..bbbac5fcd 100644 --- a/app/core/src/main/resources/settings.yml.template +++ b/app/core/src/main/resources/settings.yml.template @@ -59,7 +59,7 @@ security: idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair - jwt: + jwt: # This feature is currently under development and not yet fully supported. Do not use in production. persistence: true # Set to 'true' to enable JWT key store enableKeyRotation: true # Set to 'true' to enable key pair rotation enableKeyCleanup: true # Set to 'true' to enable key pair cleanup diff --git a/app/core/src/main/resources/static/js/fetch-utils.js b/app/core/src/main/resources/static/js/fetch-utils.js index 764a94ce1..2a2fe894c 100644 --- a/app/core/src/main/resources/static/js/fetch-utils.js +++ b/app/core/src/main/resources/static/js/fetch-utils.js @@ -1,11 +1,5 @@ // Authentication utility for cookie-based JWT window.JWTManager = { - // Check if user is authenticated (simplified for cookie-based auth) - isAuthenticated: function() { - // With cookie-based JWT, we rely on server-side validation - // This is a simplified check - actual authentication status is determined server-side - return document.cookie.includes('stirling_jwt='); - }, // Logout - clear cookies and redirect to login logout: function() { @@ -72,7 +66,3 @@ window.fetchWithCsrf = async function(url, options = {}) { return response; } -// Enhanced fetch function that always includes JWT -window.fetchWithJWT = async function(url, options = {}) { - return window.fetchWithCsrf(url, options); -} diff --git a/app/core/src/main/resources/static/js/jwt-init.js b/app/core/src/main/resources/static/js/jwt-init.js index b101e666f..8cd63e189 100644 --- a/app/core/src/main/resources/static/js/jwt-init.js +++ b/app/core/src/main/resources/static/js/jwt-init.js @@ -21,27 +21,9 @@ // Clean up any JWT tokens from URL (OAuth flow) cleanupTokenFromUrl(); - // Check if user is authenticated via cookie - if (window.JWTManager.isAuthenticated()) { - console.log('User is authenticated with JWT cookie'); - } else { - console.log('User is not authenticated'); - // Only redirect to login if we're not already on login/register pages - const currentPath = window.location.pathname; - const currentSearch = window.location.search; - // Don't redirect if we're on logout page or already being logged out - if (!currentPath.includes('/login') && - !currentPath.includes('/register') && - !currentPath.includes('/oauth') && - !currentPath.includes('/saml') && - !currentPath.includes('/error') && - !currentSearch.includes('logout=true')) { - // Redirect to login after a short delay to allow other scripts to load - setTimeout(() => { - window.location.href = '/login'; - }, 100); - } - } + // Authentication is handled server-side + // If user is not authenticated, server will redirect to login + console.log('JWT initialization complete - authentication handled server-side'); } // No form enhancement needed for cookie-based JWT @@ -51,41 +33,12 @@ // No additional processing needed } - // Add logout functionality to logout buttons - function enhanceLogoutButtons() { - document.addEventListener('click', function(event) { - const element = event.target; - - // Check if clicked element is a logout button/link - if (element.matches('a[href="/logout"], button[data-action="logout"], .logout-btn')) { - event.preventDefault(); - window.JWTManager.logout(); - } - }); - } - // Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { initializeJWT(); - enhanceLogoutButtons(); }); } else { initializeJWT(); - enhanceLogoutButtons(); } - - // Handle page visibility changes to check token expiration - document.addEventListener('visibilitychange', function() { - if (!document.hidden && !window.JWTManager.isAuthenticated()) { - // Token expired while page was hidden, redirect to login - const currentPath = window.location.pathname; - if (!currentPath.includes('/login') && - !currentPath.includes('/register') && - !currentPath.includes('/oauth') && - !currentPath.includes('/saml')) { - window.location.href = '/login'; - } - } - }); })(); \ No newline at end of file diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomAuthenticationSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomAuthenticationSuccessHandler.java index 2d36815ee..51908ef03 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomAuthenticationSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomAuthenticationSuccessHandler.java @@ -54,15 +54,11 @@ public class CustomAuthenticationSuccessHandler loginAttemptService.loginSucceeded(userName); if (jwtService.isJwtEnabled()) { - try { - String jwt = - jwtService.generateToken( - authentication, Map.of("authType", AuthenticationType.WEB)); - jwtService.addToken(response, jwt); - log.debug("JWT generated for user: {}", userName); - } catch (Exception e) { - log.error("Failed to generate JWT token for user: {}", userName, e); - } + String jwt = + jwtService.generateToken( + authentication, Map.of("authType", AuthenticationType.WEB)); + jwtService.addToken(response, jwt); + log.debug("JWT generated for user: {}", userName); getRedirectStrategy().sendRedirect(request, response, "/"); } else { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java index e820f6d37..48bcddac0 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java @@ -1,6 +1,5 @@ package stirling.software.proprietary.security.service; -import jakarta.annotation.PostConstruct; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -20,7 +19,7 @@ import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -28,6 +27,11 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.caffeine.CaffeineCache; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; + +import jakarta.annotation.PostConstruct; + +import lombok.extern.slf4j.Slf4j; + import stirling.software.common.configuration.InstallationPathConfig; import stirling.software.common.model.ApplicationProperties; import stirling.software.proprietary.security.model.JwtVerificationKey; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterface.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterface.java index 43aa9c7c7..f3050472e 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterface.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterface.java @@ -7,6 +7,7 @@ import java.security.spec.InvalidKeySpecException; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; + import stirling.software.proprietary.security.model.JwtVerificationKey; public interface KeyPersistenceServiceInterface { diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/CustomLogoutSuccessHandlerTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/CustomLogoutSuccessHandlerTest.java index 756405c20..7a4076260 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/CustomLogoutSuccessHandlerTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/CustomLogoutSuccessHandlerTest.java @@ -1,18 +1,22 @@ package stirling.software.proprietary.security; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import static org.mockito.Mockito.*; + import java.io.IOException; + 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.security.oauth2.client.authentication.OAuth2AuthenticationToken; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import stirling.software.common.configuration.AppConfig; import stirling.software.common.model.ApplicationProperties; import stirling.software.proprietary.security.service.JwtServiceInterface; -import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class CustomLogoutSuccessHandlerTest { diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/JwtAuthenticationEntryPointTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/JwtAuthenticationEntryPointTest.java index 08abd1965..a47f45318 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/JwtAuthenticationEntryPointTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/JwtAuthenticationEntryPointTest.java @@ -1,32 +1,30 @@ package stirling.software.proprietary.security; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import static org.mockito.Mockito.*; + +import java.io.IOException; + 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 java.io.IOException; -import stirling.software.proprietary.security.model.exception.AuthenticationFailureException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; -import static org.mockito.Mockito.*; +import stirling.software.proprietary.security.model.exception.AuthenticationFailureException; @ExtendWith(MockitoExtension.class) class JwtAuthenticationEntryPointTest { - @Mock - private HttpServletRequest request; + @Mock private HttpServletRequest request; - @Mock - private HttpServletResponse response; + @Mock private HttpServletResponse response; - @Mock - private AuthenticationFailureException authException; + @Mock private AuthenticationFailureException authException; - @InjectMocks - private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + @InjectMocks private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Test void testCommence() throws IOException { diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java index 4cb47816b..d3f484486 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilterTest.java @@ -1,12 +1,21 @@ package stirling.software.proprietary.security.filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.IOException; import java.util.Collections; import java.util.Map; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -20,61 +29,43 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.AuthenticationEntryPoint; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import stirling.software.common.model.ApplicationProperties; import stirling.software.proprietary.security.model.exception.AuthenticationFailureException; import stirling.software.proprietary.security.service.CustomUserDetailsService; import stirling.software.proprietary.security.service.JwtServiceInterface; import stirling.software.proprietary.security.service.UserService; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @Disabled @ExtendWith(MockitoExtension.class) class JwtAuthenticationFilterTest { - @Mock - private JwtServiceInterface jwtService; + @Mock private JwtServiceInterface jwtService; - @Mock - private CustomUserDetailsService userDetailsService; + @Mock private CustomUserDetailsService userDetailsService; - @Mock - private UserService userService; + @Mock private UserService userService; - @Mock - private ApplicationProperties.Security securityProperties; + @Mock private ApplicationProperties.Security securityProperties; - @Mock - private HttpServletRequest request; + @Mock private HttpServletRequest request; - @Mock - private HttpServletResponse response; + @Mock private HttpServletResponse response; - @Mock - private FilterChain filterChain; + @Mock private FilterChain filterChain; - @Mock - private UserDetails userDetails; + @Mock private UserDetails userDetails; - @Mock - private SecurityContext securityContext; + @Mock private SecurityContext securityContext; - @Mock - private AuthenticationEntryPoint authenticationEntryPoint; + @Mock private AuthenticationEntryPoint authenticationEntryPoint; - @InjectMocks - private JwtAuthenticationFilter jwtAuthenticationFilter; + @InjectMocks private JwtAuthenticationFilter jwtAuthenticationFilter; @Test void shouldNotAuthenticateWhenJwtDisabled() throws ServletException, IOException { @@ -113,21 +104,29 @@ class JwtAuthenticationFilterTest { when(userDetails.getAuthorities()).thenReturn(Collections.emptyList()); when(userDetailsService.loadUserByUsername(username)).thenReturn(userDetails); - try (MockedStatic mockedSecurityContextHolder = mockStatic(SecurityContextHolder.class)) { + try (MockedStatic mockedSecurityContextHolder = + mockStatic(SecurityContextHolder.class)) { UsernamePasswordAuthenticationToken authToken = - new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); when(securityContext.getAuthentication()).thenReturn(null).thenReturn(authToken); - mockedSecurityContextHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext); - when(jwtService.generateToken(any(UsernamePasswordAuthenticationToken.class), eq(claims))).thenReturn(newToken); + mockedSecurityContextHolder + .when(SecurityContextHolder::getContext) + .thenReturn(securityContext); + when(jwtService.generateToken( + any(UsernamePasswordAuthenticationToken.class), eq(claims))) + .thenReturn(newToken); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); verify(jwtService).validateToken(token); verify(jwtService).extractClaims(token); verify(userDetailsService).loadUserByUsername(username); - verify(securityContext).setAuthentication(any(UsernamePasswordAuthenticationToken.class)); - verify(jwtService).generateToken(any(UsernamePasswordAuthenticationToken.class), eq(claims)); + verify(securityContext) + .setAuthentication(any(UsernamePasswordAuthenticationToken.class)); + verify(jwtService) + .generateToken(any(UsernamePasswordAuthenticationToken.class), eq(claims)); verify(jwtService).addToken(response, newToken); verify(filterChain).doFilter(request, response); } @@ -154,12 +153,15 @@ class JwtAuthenticationFilterTest { when(request.getRequestURI()).thenReturn("/protected"); when(request.getContextPath()).thenReturn("/"); when(jwtService.extractToken(request)).thenReturn(token); - doThrow(new AuthenticationFailureException("Invalid token")).when(jwtService).validateToken(token); + doThrow(new AuthenticationFailureException("Invalid token")) + .when(jwtService) + .validateToken(token); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); verify(jwtService).validateToken(token); - verify(authenticationEntryPoint).commence(eq(request), eq(response), any(AuthenticationFailureException.class)); + verify(authenticationEntryPoint) + .commence(eq(request), eq(response), any(AuthenticationFailureException.class)); verify(filterChain, never()).doFilter(request, response); } @@ -171,7 +173,9 @@ class JwtAuthenticationFilterTest { when(request.getRequestURI()).thenReturn("/protected"); when(request.getContextPath()).thenReturn("/"); when(jwtService.extractToken(request)).thenReturn(token); - doThrow(new AuthenticationFailureException("The token has expired")).when(jwtService).validateToken(token); + doThrow(new AuthenticationFailureException("The token has expired")) + .when(jwtService) + .validateToken(token); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); @@ -194,11 +198,19 @@ class JwtAuthenticationFilterTest { when(jwtService.extractClaims(token)).thenReturn(claims); when(userDetailsService.loadUserByUsername(username)).thenReturn(null); - try (MockedStatic mockedSecurityContextHolder = mockStatic(SecurityContextHolder.class)) { + try (MockedStatic mockedSecurityContextHolder = + mockStatic(SecurityContextHolder.class)) { when(securityContext.getAuthentication()).thenReturn(null); - mockedSecurityContextHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext); + mockedSecurityContextHolder + .when(SecurityContextHolder::getContext) + .thenReturn(securityContext); - UsernameNotFoundException result = assertThrows(UsernameNotFoundException.class, () -> jwtAuthenticationFilter.doFilterInternal(request, response, filterChain)); + UsernameNotFoundException result = + assertThrows( + UsernameNotFoundException.class, + () -> + jwtAuthenticationFilter.doFilterInternal( + request, response, filterChain)); assertEquals("User not found: " + username, result.getMessage()); verify(userDetailsService).loadUserByUsername(username); @@ -207,7 +219,8 @@ class JwtAuthenticationFilterTest { } @Test - void testAuthenticationEntryPointCalledWithCorrectException() throws ServletException, IOException { + void testAuthenticationEntryPointCalledWithCorrectException() + throws ServletException, IOException { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); when(request.getContextPath()).thenReturn("/"); @@ -215,9 +228,15 @@ class JwtAuthenticationFilterTest { jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); - verify(authenticationEntryPoint).commence(eq(request), eq(response), argThat(exception -> - exception.getMessage().equals("JWT is missing from the request") - )); + verify(authenticationEntryPoint) + .commence( + eq(request), + eq(response), + argThat( + exception -> + exception + .getMessage() + .equals("JWT is missing from the request"))); verify(filterChain, never()).doFilter(request, response); } } diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepositoryTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepositoryTest.java index f08d33d8c..1aa083cc0 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepositoryTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepositoryTest.java @@ -1,23 +1,5 @@ package stirling.software.proprietary.security.saml2; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata; -import stirling.software.proprietary.security.service.JwtServiceInterface; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -28,6 +10,28 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest; +import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import stirling.software.proprietary.security.service.JwtServiceInterface; + @ExtendWith(MockitoExtension.class) class JwtSaml2AuthenticationRequestRepositoryTest { @@ -35,19 +39,18 @@ class JwtSaml2AuthenticationRequestRepositoryTest { private Map tokenStore; - @Mock - private JwtServiceInterface jwtService; + @Mock private JwtServiceInterface jwtService; - @Mock - private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository; + @Mock private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository; private JwtSaml2AuthenticationRequestRepository jwtSaml2AuthenticationRequestRepository; @BeforeEach void setUp() { tokenStore = new ConcurrentHashMap<>(); - jwtSaml2AuthenticationRequestRepository = new JwtSaml2AuthenticationRequestRepository( - tokenStore, jwtService, relyingPartyRegistrationRepository); + jwtSaml2AuthenticationRequestRepository = + new JwtSaml2AuthenticationRequestRepository( + tokenStore, jwtService, relyingPartyRegistrationRepository); } @Test @@ -71,7 +74,8 @@ class JwtSaml2AuthenticationRequestRepositoryTest { when(authRequest.getRelyingPartyRegistrationId()).thenReturn(relyingPartyRegistrationId); when(jwtService.generateToken(eq(""), anyMap())).thenReturn(token); - jwtSaml2AuthenticationRequestRepository.saveAuthenticationRequest(authRequest, request, response); + jwtSaml2AuthenticationRequestRepository.saveAuthenticationRequest( + authRequest, request, response); verify(request).setAttribute(SAML_REQUEST_TOKEN, relayState); verify(response).addHeader(SAML_REQUEST_TOKEN, relayState); @@ -94,20 +98,23 @@ class JwtSaml2AuthenticationRequestRepositoryTest { var assertingPartyMetadata = mock(AssertingPartyMetadata.class); String relayState = "testRelayState"; String token = "testToken"; - Map claims = Map.of( - "id", "testId", - "relyingPartyRegistrationId", "stirling-pdf", - "authenticationRequestUri", "example.com/authnRequest", - "samlRequest", "testSamlRequest", - "relayState", relayState - ); + Map claims = + Map.of( + "id", "testId", + "relyingPartyRegistrationId", "stirling-pdf", + "authenticationRequestUri", "example.com/authnRequest", + "samlRequest", "testSamlRequest", + "relayState", relayState); when(request.getParameter("RelayState")).thenReturn(relayState); when(jwtService.extractClaims(token)).thenReturn(claims); - when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")).thenReturn(relyingPartyRegistration); + when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")) + .thenReturn(relyingPartyRegistration); when(relyingPartyRegistration.getRegistrationId()).thenReturn("stirling-pdf"); - when(relyingPartyRegistration.getAssertingPartyMetadata()).thenReturn(assertingPartyMetadata); - when(assertingPartyMetadata.getSingleSignOnServiceLocation()).thenReturn("https://example.com/sso"); + when(relyingPartyRegistration.getAssertingPartyMetadata()) + .thenReturn(assertingPartyMetadata); + when(assertingPartyMetadata.getSingleSignOnServiceLocation()) + .thenReturn("https://example.com/sso"); tokenStore.put(relayState, token); var result = jwtSaml2AuthenticationRequestRepository.loadAuthenticationRequest(request); @@ -142,17 +149,18 @@ class JwtSaml2AuthenticationRequestRepositoryTest { var request = mock(MockHttpServletRequest.class); String relayState = "testRelayState"; String token = "testToken"; - Map claims = Map.of( - "id", "testId", - "relyingPartyRegistrationId", "stirling-pdf", - "authenticationRequestUri", "example.com/authnRequest", - "samlRequest", "testSamlRequest", - "relayState", relayState - ); + Map claims = + Map.of( + "id", "testId", + "relyingPartyRegistrationId", "stirling-pdf", + "authenticationRequestUri", "example.com/authnRequest", + "samlRequest", "testSamlRequest", + "relayState", relayState); when(request.getParameter("RelayState")).thenReturn(relayState); when(jwtService.extractClaims(token)).thenReturn(claims); - when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")).thenReturn(null); + when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")) + .thenReturn(null); tokenStore.put(relayState, token); var result = jwtSaml2AuthenticationRequestRepository.loadAuthenticationRequest(request); @@ -168,23 +176,28 @@ class JwtSaml2AuthenticationRequestRepositoryTest { var assertingPartyMetadata = mock(AssertingPartyMetadata.class); String relayState = "testRelayState"; String token = "testToken"; - Map claims = Map.of( - "id", "testId", - "relyingPartyRegistrationId", "stirling-pdf", - "authenticationRequestUri", "example.com/authnRequest", - "samlRequest", "testSamlRequest", - "relayState", relayState - ); + Map claims = + Map.of( + "id", "testId", + "relyingPartyRegistrationId", "stirling-pdf", + "authenticationRequestUri", "example.com/authnRequest", + "samlRequest", "testSamlRequest", + "relayState", relayState); when(request.getParameter("RelayState")).thenReturn(relayState); when(jwtService.extractClaims(token)).thenReturn(claims); - when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")).thenReturn(relyingPartyRegistration); + when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")) + .thenReturn(relyingPartyRegistration); when(relyingPartyRegistration.getRegistrationId()).thenReturn("stirling-pdf"); - when(relyingPartyRegistration.getAssertingPartyMetadata()).thenReturn(assertingPartyMetadata); - when(assertingPartyMetadata.getSingleSignOnServiceLocation()).thenReturn("https://example.com/sso"); + when(relyingPartyRegistration.getAssertingPartyMetadata()) + .thenReturn(assertingPartyMetadata); + when(assertingPartyMetadata.getSingleSignOnServiceLocation()) + .thenReturn("https://example.com/sso"); tokenStore.put(relayState, token); - var result = jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest(request, response); + var result = + jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest( + request, response); assertNotNull(result); assertFalse(tokenStore.containsKey(relayState)); @@ -196,7 +209,9 @@ class JwtSaml2AuthenticationRequestRepositoryTest { var response = mock(HttpServletResponse.class); when(request.getParameter("RelayState")).thenReturn(null); - var result = jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest(request, response); + var result = + jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest( + request, response); assertNull(result); } @@ -207,7 +222,9 @@ class JwtSaml2AuthenticationRequestRepositoryTest { var response = mock(HttpServletResponse.class); when(request.getParameter("RelayState")).thenReturn("nonExistentRelayState"); - var result = jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest(request, response); + var result = + jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest( + request, response); assertNull(result); } @@ -220,7 +237,9 @@ class JwtSaml2AuthenticationRequestRepositoryTest { when(request.getParameter("RelayState")).thenReturn(relayState); - var result = jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest(request, response); + var result = + jwtSaml2AuthenticationRequestRepository.removeAuthenticationRequest( + request, response); assertNull(result); assertFalse(tokenStore.containsKey(relayState)); diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtServiceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtServiceTest.java index ae7023381..6f9af4c54 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtServiceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtServiceTest.java @@ -1,29 +1,5 @@ package stirling.software.proprietary.security.service; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; -import java.util.Collections; -import java.util.Optional; -import stirling.software.proprietary.security.model.JwtVerificationKey; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.core.Authentication; -import stirling.software.proprietary.security.model.User; -import stirling.software.proprietary.security.model.exception.AuthenticationFailureException; - -import java.util.HashMap; -import java.util.Map; - import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -39,23 +15,44 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import stirling.software.proprietary.security.model.JwtVerificationKey; +import stirling.software.proprietary.security.model.User; +import stirling.software.proprietary.security.model.exception.AuthenticationFailureException; + @ExtendWith(MockitoExtension.class) class JwtServiceTest { - @Mock - private Authentication authentication; + @Mock private Authentication authentication; - @Mock - private User userDetails; + @Mock private User userDetails; - @Mock - private HttpServletRequest request; + @Mock private HttpServletRequest request; - @Mock - private HttpServletResponse response; + @Mock private HttpServletResponse response; - @Mock - private KeyPersistenceServiceInterface keystoreService; + @Mock private KeyPersistenceServiceInterface keystoreService; private JwtService jwtService; private KeyPair testKeyPair; @@ -67,9 +64,10 @@ class JwtServiceTest { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); testKeyPair = keyPairGenerator.generateKeyPair(); - + // Create test verification key - String encodedPublicKey = Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); + String encodedPublicKey = + Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); testVerificationKey = new JwtVerificationKey("test-key-id", encodedPublicKey); jwtService = new JwtService(true, keystoreService); @@ -81,7 +79,8 @@ class JwtServiceTest { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -101,7 +100,8 @@ class JwtServiceTest { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -120,7 +120,8 @@ class JwtServiceTest { void testValidateTokenSuccess() throws Exception { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn("testuser"); @@ -132,21 +133,28 @@ class JwtServiceTest { @Test void testValidateTokenWithInvalidToken() throws Exception { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); - assertThrows(AuthenticationFailureException.class, () -> { - jwtService.validateToken("invalid-token"); - }); + assertThrows( + AuthenticationFailureException.class, + () -> { + jwtService.validateToken("invalid-token"); + }); } @Test void testValidateTokenWithMalformedToken() throws Exception { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); - AuthenticationFailureException exception = assertThrows(AuthenticationFailureException.class, () -> { - jwtService.validateToken("malformed.token"); - }); + AuthenticationFailureException exception = + assertThrows( + AuthenticationFailureException.class, + () -> { + jwtService.validateToken("malformed.token"); + }); assertTrue(exception.getMessage().contains("Invalid")); } @@ -154,13 +162,19 @@ class JwtServiceTest { @Test void testValidateTokenWithEmptyToken() throws Exception { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); - AuthenticationFailureException exception = assertThrows(AuthenticationFailureException.class, () -> { - jwtService.validateToken(""); - }); + AuthenticationFailureException exception = + assertThrows( + AuthenticationFailureException.class, + () -> { + jwtService.validateToken(""); + }); - assertTrue(exception.getMessage().contains("Claims are empty") || exception.getMessage().contains("Invalid")); + assertTrue( + exception.getMessage().contains("Claims are empty") + || exception.getMessage().contains("Invalid")); } @Test @@ -171,7 +185,8 @@ class JwtServiceTest { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); when(authentication.getPrincipal()).thenReturn(user); when(user.getUsername()).thenReturn(username); @@ -183,9 +198,12 @@ class JwtServiceTest { @Test void testExtractUsernameWithInvalidToken() throws Exception { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); - assertThrows(AuthenticationFailureException.class, () -> jwtService.extractUsername("invalid-token")); + assertThrows( + AuthenticationFailureException.class, + () -> jwtService.extractUsername("invalid-token")); } @Test @@ -195,7 +213,8 @@ class JwtServiceTest { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -211,15 +230,18 @@ class JwtServiceTest { @Test void testExtractClaimsWithInvalidToken() throws Exception { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); - assertThrows(AuthenticationFailureException.class, () -> jwtService.extractClaims("invalid-token")); + assertThrows( + AuthenticationFailureException.class, + () -> jwtService.extractClaims("invalid-token")); } @Test void testExtractTokenWithCookie() { String token = "test-token"; - Cookie[] cookies = { new Cookie("stirling_jwt", token) }; + Cookie[] cookies = {new Cookie("stirling_jwt", token)}; when(request.getCookies()).thenReturn(cookies); assertEquals(token, jwtService.extractToken(request)); @@ -299,7 +321,8 @@ class JwtServiceTest { when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -307,7 +330,9 @@ class JwtServiceTest { String token = jwtService.generateToken(authentication, claims); // Mock extraction of key ID and verification (lenient to avoid unused stubbing) - lenient().when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); + lenient() + .when(keystoreService.getKeyPair("test-key-id")) + .thenReturn(Optional.of(testKeyPair)); // Verify token can be validated assertDoesNotThrow(() -> jwtService.validateToken(token)); @@ -322,7 +347,8 @@ class JwtServiceTest { // First, generate a token successfully when(keystoreService.getActiveKey()).thenReturn(testVerificationKey); when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair)); - when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())).thenReturn(testKeyPair.getPublic()); + when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey())) + .thenReturn(testKeyPair.getPublic()); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -330,9 +356,11 @@ class JwtServiceTest { // Now mock the scenario for validation - key not found, but fallback works // Create a fallback key pair that can be used - JwtVerificationKey fallbackKey = new JwtVerificationKey("fallback-key", - Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded())); - + JwtVerificationKey fallbackKey = + new JwtVerificationKey( + "fallback-key", + Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded())); + // Mock the specific key lookup to fail, but the active key should work when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.empty()); when(keystoreService.refreshActiveKeyPair()).thenReturn(fallbackKey); @@ -351,10 +379,11 @@ class JwtServiceTest { JwtService testService = new JwtService(true, keystoreService); // Set the secureCookie field using reflection - java.lang.reflect.Field secureCookieField = JwtService.class.getDeclaredField("secureCookie"); + java.lang.reflect.Field secureCookieField = + JwtService.class.getDeclaredField("secureCookie"); secureCookieField.setAccessible(true); secureCookieField.set(testService, secureCookie); return testService; } -} \ No newline at end of file +} diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterfaceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterfaceTest.java index 04632d0ea..33b971e5a 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterfaceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/KeyPersistenceServiceInterfaceTest.java @@ -1,5 +1,13 @@ package stirling.software.proprietary.security.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -8,6 +16,7 @@ import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -19,31 +28,21 @@ import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.cache.CacheManager; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; + import stirling.software.common.configuration.InstallationPathConfig; import stirling.software.common.model.ApplicationProperties; import stirling.software.proprietary.security.model.JwtVerificationKey; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class KeyPersistenceServiceInterfaceTest { - @Mock - private ApplicationProperties applicationProperties; + @Mock private ApplicationProperties applicationProperties; - @Mock - private ApplicationProperties.Security security; + @Mock private ApplicationProperties.Security security; - @Mock - private ApplicationProperties.Security.Jwt jwtConfig; + @Mock private ApplicationProperties.Security.Jwt jwtConfig; - @TempDir - Path tempDir; + @TempDir Path tempDir; private KeyPersistenceService keyPersistenceService; private KeyPair testKeyPair; @@ -67,8 +66,11 @@ class KeyPersistenceServiceInterfaceTest { void testKeystoreEnabled(boolean keystoreEnabled) { when(jwtConfig.isEnableKeystore()).thenReturn(keystoreEnabled); - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); assertEquals(keystoreEnabled, keyPersistenceService.isKeystoreEnabled()); @@ -77,8 +79,11 @@ class KeyPersistenceServiceInterfaceTest { @Test void testGetActiveKeypairWhenNoActiveKeyExists() { - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); keyPersistenceService.initializeKeystore(); @@ -93,16 +98,21 @@ class KeyPersistenceServiceInterfaceTest { @Test void testGetActiveKeyPairWithExistingKey() throws Exception { String keyId = "test-key-2024-01-01-120000"; - String publicKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); - String privateKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded()); + String publicKeyBase64 = + Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); + String privateKeyBase64 = + Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded()); JwtVerificationKey existingKey = new JwtVerificationKey(keyId, publicKeyBase64); Path keyFile = tempDir.resolve(keyId + ".key"); Files.writeString(keyFile, privateKeyBase64); - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); keyPersistenceService.initializeKeystore(); @@ -116,19 +126,27 @@ class KeyPersistenceServiceInterfaceTest { @Test void testGetKeyPair() throws Exception { String keyId = "test-key-123"; - String publicKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); - String privateKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded()); + String publicKeyBase64 = + Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); + String privateKeyBase64 = + Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded()); JwtVerificationKey signingKey = new JwtVerificationKey(keyId, publicKeyBase64); Path keyFile = tempDir.resolve(keyId + ".key"); Files.writeString(keyFile, privateKeyBase64); - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); - keyPersistenceService.getClass().getDeclaredField("verifyingKeyCache").setAccessible(true); + keyPersistenceService + .getClass() + .getDeclaredField("verifyingKeyCache") + .setAccessible(true); var cache = cacheManager.getCache("verifyingKeys"); cache.put(keyId, signingKey); @@ -144,8 +162,11 @@ class KeyPersistenceServiceInterfaceTest { void testGetKeyPairNotFound() { String keyId = "non-existent-key"; - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); Optional result = keyPersistenceService.getKeyPair(keyId); @@ -158,8 +179,11 @@ class KeyPersistenceServiceInterfaceTest { void testGetKeyPairWhenKeystoreDisabled() { when(jwtConfig.isEnableKeystore()).thenReturn(false); - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); Optional result = keyPersistenceService.getKeyPair("any-key"); @@ -170,8 +194,11 @@ class KeyPersistenceServiceInterfaceTest { @Test void testInitializeKeystoreCreatesDirectory() throws IOException { - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); keyPersistenceService.initializeKeystore(); @@ -183,12 +210,16 @@ class KeyPersistenceServiceInterfaceTest { @Test void testLoadExistingKeypairWithMissingPrivateKeyFile() throws Exception { String keyId = "test-key-missing-file"; - String publicKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); + String publicKeyBase64 = + Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); JwtVerificationKey existingKey = new JwtVerificationKey(keyId, publicKeyBase64); - try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { - mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); + try (MockedStatic mockedStatic = + mockStatic(InstallationPathConfig.class)) { + mockedStatic + .when(InstallationPathConfig::getPrivateKeyPath) + .thenReturn(tempDir.toString()); keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager); keyPersistenceService.initializeKeystore();