From 9d162abc8c4e22cbd2b454fe4bae890d04602527 Mon Sep 17 00:00:00 2001 From: Dario Ghunney Ware Date: Tue, 29 Jul 2025 20:56:26 +0100 Subject: [PATCH] Deactivating keys --- .../common/model/ApplicationProperties.java | 4 +- .../src/main/resources/settings.yml.template | 5 +- .../CustomAuthenticationSuccessHandler.java | 2 +- .../security/CustomLogoutSuccessHandler.java | 4 +- .../repository/JwtSigningKeyRepository.java | 12 ++-- .../filter/JwtAuthenticationFilter.java | 8 +-- ...tomOAuth2AuthenticationSuccessHandler.java | 2 +- ...stomSaml2AuthenticationSuccessHandler.java | 2 +- ...tSaml2AuthenticationRequestRepository.java | 2 +- .../service/JwtKeyCleanupService.java | 8 +-- .../security/service/JwtKeystoreService.java | 38 +++++++--- .../service/JwtKeystoreServiceInterface.java | 6 +- .../security/service/JwtService.java | 62 +++++++++------- .../security/service/JwtServiceInterface.java | 8 +-- .../CustomLogoutSuccessHandlerTest.java | 10 +-- .../filter/JwtAuthenticationFilterTest.java | 22 +++--- ...l2AuthenticationRequestRepositoryTest.java | 8 +-- .../service/JwtKeyCleanupServiceTest.java | 16 ++--- .../JwtKeystoreServiceInterfaceTest.java | 32 ++++----- .../security/service/JwtServiceTest.java | 72 +++++++++---------- 20 files changed, 177 insertions(+), 146 deletions(-) diff --git a/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java b/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java index 48df4f948..d37a7595b 100644 --- a/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java +++ b/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java @@ -304,8 +304,8 @@ public class ApplicationProperties { private boolean enableKeystore = true; private boolean enableKeyRotation = false; private boolean enableKeyCleanup = true; - private int keyRetentionDays = 7; - private int cleanupBatchSize = 100; + private int keyRetentionDays; + private int cleanupBatchSize; } } diff --git a/app/core/src/main/resources/settings.yml.template b/app/core/src/main/resources/settings.yml.template index 0814a6924..9ec94beb3 100644 --- a/app/core/src/main/resources/settings.yml.template +++ b/app/core/src/main/resources/settings.yml.template @@ -62,9 +62,12 @@ security: jwt: enableKeyStore: true # Set to 'true' to enable JWT key store enableKeyRotation: true # Set to 'true' to enable JWT key rotation + enableKeyCleanup: true # Set to 'true' to enable JWT key cleanup + keyRetentionDays: 7 # Number of days to retain old keys + cleanupBatchSize: 100 # Number of keys to clean up in each batch premium: - key: 3R3T-WFPY-UNRW-LJFA-MMXM-YVJK-WCKY-PCRT # fixme: remove + key: 00000000-0000-0000-0000-000000000000 enabled: false # Enable license key checks for pro/enterprise features proFeatures: SSOAutoLogin: false 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 63c653a3a..2d36815ee 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 @@ -58,7 +58,7 @@ public class CustomAuthenticationSuccessHandler String jwt = jwtService.generateToken( authentication, Map.of("authType", AuthenticationType.WEB)); - jwtService.addTokenToResponse(response, jwt); + 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); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java index aaf6ea2c3..136120528 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java @@ -71,8 +71,8 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { authentication.getClass().getSimpleName()); getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH); } - } else if (!jwtService.extractTokenFromRequest(request).isBlank()) { - jwtService.clearTokenFromResponse(response); + } else if (!jwtService.extractToken(request).isBlank()) { + jwtService.clearToken(response); getRedirectStrategy().sendRedirect(request, response, LOGOUT_PATH); } else { // Redirect to login page after logout diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/database/repository/JwtSigningKeyRepository.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/database/repository/JwtSigningKeyRepository.java index ee7c3b419..7fcb5503a 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/database/repository/JwtSigningKeyRepository.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/database/repository/JwtSigningKeyRepository.java @@ -16,20 +16,18 @@ import stirling.software.proprietary.security.model.JwtSigningKey; @Repository public interface JwtSigningKeyRepository extends JpaRepository { - Optional findByIsActiveTrue(); + Optional findFirstByIsActiveTrueOrderByCreatedAtDesc(); Optional findByKeyId(String keyId); - @Query( - "SELECT k FROM signing_keys k WHERE k.isActive = false AND k.createdAt < :cutoffDate ORDER BY k.createdAt ASC") - List findInactiveKeysOlderThan( + @Query("SELECT k FROM JwtSigningKey k WHERE k.createdAt < :cutoffDate ORDER BY k.createdAt ASC") + List findKeysOlderThan( @Param("cutoffDate") LocalDateTime cutoffDate, Pageable pageable); - @Query( - "SELECT COUNT(k) FROM signing_keys k WHERE k.isActive = false AND k.createdAt < :cutoffDate") + @Query("SELECT COUNT(k) FROM JwtSigningKey k WHERE k.createdAt < :cutoffDate") long countKeysEligibleForCleanup(@Param("cutoffDate") LocalDateTime cutoffDate); @Modifying - @Query("DELETE FROM signing_keys k WHERE k.id IN :ids") + @Query("DELETE FROM JwtSigningKey k WHERE k.id IN :ids") void deleteAllByIdInBatch(@Param("ids") List ids); } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java index 6aea4739a..4a0115c1a 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/filter/JwtAuthenticationFilter.java @@ -68,7 +68,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { return; } - String jwtToken = jwtService.extractTokenFromRequest(request); + String jwtToken = jwtService.extractToken(request); if (jwtToken == null) { // If they are unauthenticated and navigating to '/', redirect to '/login' instead of @@ -89,19 +89,19 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { jwtService.validateToken(jwtToken); } catch (AuthenticationFailureException e) { // Clear invalid tokens from response - jwtService.clearTokenFromResponse(response); + jwtService.clearToken(response); handleAuthenticationFailure(request, response, e); return; } - Map claims = jwtService.extractAllClaims(jwtToken); + Map claims = jwtService.extractClaims(jwtToken); String tokenUsername = claims.get("sub").toString(); try { Authentication authentication = createAuthentication(request, claims); String jwt = jwtService.generateToken(authentication, claims); - jwtService.addTokenToResponse(response, jwt); + jwtService.addToken(response, jwt); } catch (SQLException | UnsupportedProviderException e) { log.error("Error processing user authentication for user: {}", tokenUsername, e); handleAuthenticationFailure( diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java index dc489ffd2..4e7ed9d9e 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/oauth2/CustomOAuth2AuthenticationSuccessHandler.java @@ -76,7 +76,7 @@ public class CustomOAuth2AuthenticationSuccessHandler String jwt = jwtService.generateToken( authentication, Map.of("authType", AuthenticationType.OAUTH2)); - jwtService.addTokenToResponse(response, jwt); + jwtService.addToken(response, jwt); } if (userService.isUserDisabled(username)) { getRedirectStrategy() diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java index 35ce832e9..57d667aa1 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/CustomSaml2AuthenticationSuccessHandler.java @@ -141,7 +141,7 @@ public class CustomSaml2AuthenticationSuccessHandler String jwt = jwtService.generateToken( authentication, Map.of("authType", AuthenticationType.SAML2)); - jwtService.addTokenToResponse(response, jwt); + jwtService.addToken(response, jwt); } } } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java index 68c190e64..d0508151c 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/JwtSaml2AuthenticationRequestRepository.java @@ -68,7 +68,7 @@ public class JwtSaml2AuthenticationRequestRepository return null; } - Map claims = jwtService.extractAllClaims(token); + Map claims = jwtService.extractClaims(token); return deserializeSamlRequest(claims); } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeyCleanupService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeyCleanupService.java index 706f7a537..6f5a43b8c 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeyCleanupService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeyCleanupService.java @@ -42,14 +42,14 @@ public class JwtKeyCleanupService { } @Transactional - @Scheduled(fixedDelay = 1, timeUnit = TimeUnit.MINUTES) + @Scheduled(fixedDelay = 24, timeUnit = TimeUnit.HOURS) public void cleanup() { if (!jwtProperties.isEnableKeyCleanup() || !keystoreService.isKeystoreEnabled()) { - log.debug("Key cleanup is disabled, skipping cleanup"); + log.debug("Key cleanup is disabled"); return; } - log.info("Removing inactive keys older than {} days", jwtProperties.getKeyRetentionDays()); + log.info("Removing keys older than {} day(s)", jwtProperties.getKeyRetentionDays()); try { LocalDateTime cutoffDate = @@ -75,7 +75,7 @@ public class JwtKeyCleanupService { while (true) { Pageable pageable = PageRequest.of(0, batchSize); List keysToCleanup = - signingKeyRepository.findInactiveKeysOlderThan(cutoffDate, pageable); + signingKeyRepository.findKeysOlderThan(cutoffDate, pageable); if (keysToCleanup.isEmpty()) { break; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreService.java index d1d3279bc..64c1900e6 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreService.java @@ -20,6 +20,7 @@ import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import jakarta.annotation.PostConstruct; @@ -35,7 +36,7 @@ import stirling.software.proprietary.security.model.JwtSigningKey; public class JwtKeystoreService implements JwtKeystoreServiceInterface { public static final String KEY_SUFFIX = ".key"; - private final JwtSigningKeyRepository repository; + private final JwtSigningKeyRepository signingKeyRepository; private final ApplicationProperties.Security.Jwt jwtProperties; private volatile KeyPair currentKeyPair; @@ -43,8 +44,9 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { @Autowired public JwtKeystoreService( - JwtSigningKeyRepository repository, ApplicationProperties applicationProperties) { - this.repository = repository; + JwtSigningKeyRepository signingKeyRepository, + ApplicationProperties applicationProperties) { + this.signingKeyRepository = signingKeyRepository; this.jwtProperties = applicationProperties.getSecurity().getJwt(); } @@ -64,7 +66,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { } @Override - public KeyPair getActiveKeypair() { + public KeyPair getActiveKeyPair() { if (!isKeystoreEnabled() || currentKeyPair == null) { return generateRSAKeypair(); } @@ -72,20 +74,25 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { } @Override - public Optional getKeypairByKeyId(String keyId) { + public Optional getKeyPairByKeyId(String keyId) { if (!isKeystoreEnabled()) { + log.debug("Keystore is disabled, cannot lookup key by ID: {}", keyId); return Optional.empty(); } try { - Optional signingKey = repository.findByKeyId(keyId); + log.debug("Looking up signing key in database for keyId: {}", keyId); + Optional signingKey = signingKeyRepository.findByKeyId(keyId); if (signingKey.isEmpty()) { + log.warn("No signing key found in database for keyId: {}", keyId); return Optional.empty(); } + log.debug("Found signing key in database, loading private key for keyId: {}", keyId); PrivateKey privateKey = loadPrivateKey(keyId); PublicKey publicKey = decodePublicKey(signingKey.get().getSigningKey()); + log.debug("Successfully loaded key pair for keyId: {}", keyId); return Optional.of(new KeyPair(publicKey, privateKey)); } catch (Exception e) { log.error("Failed to load keypair for keyId: {}", keyId, e); @@ -104,7 +111,8 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { } private void loadOrGenerateKeypair() { - Optional activeKey = repository.findByIsActiveTrue(); + Optional activeKey = + signingKeyRepository.findFirstByIsActiveTrueOrderByCreatedAtDesc(); if (activeKey.isPresent()) { try { @@ -112,9 +120,10 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { PrivateKey privateKey = loadPrivateKey(currentKeyId); PublicKey publicKey = decodePublicKey(activeKey.get().getSigningKey()); currentKeyPair = new KeyPair(publicKey, privateKey); - log.info("Loaded existing keypair with keyId: {}", currentKeyId); + + log.info("Loaded existing keypair: {}", currentKeyId); } catch (Exception e) { - log.error("Failed to load existing keypair, generating new one", e); + log.error("Failed to load existing keypair, generating new keypair", e); generateAndStoreKeypair(); } } else { @@ -122,6 +131,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { } } + @Transactional private void generateAndStoreKeypair() { try { KeyPair keyPair = generateRSAKeypair(); @@ -131,12 +141,12 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { JwtSigningKey signingKey = new JwtSigningKey(keyId, encodePublicKey(keyPair.getPublic()), "RS256"); - repository.save(signingKey); + signingKeyRepository.save(signingKey); currentKeyPair = keyPair; currentKeyId = keyId; log.info("Generated and stored new keypair with keyId: {}", keyId); - } catch (Exception e) { + } catch (IOException e) { log.error("Failed to generate and store keypair", e); throw new RuntimeException("Keypair generation failed", e); } @@ -155,6 +165,12 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { return keyPairGenerator.generateKeyPair(); } + @Override + public KeyPair refreshKeyPairs() { + generateAndStoreKeypair(); + return currentKeyPair; + } + private String generateKeyId() { return "jwt-key-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HHmmss")); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterface.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterface.java index dfb341b28..4cf9d8f55 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterface.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterface.java @@ -5,11 +5,13 @@ import java.util.Optional; public interface JwtKeystoreServiceInterface { - KeyPair getActiveKeypair(); + KeyPair getActiveKeyPair(); - Optional getKeypairByKeyId(String keyId); + Optional getKeyPairByKeyId(String keyId); String getActiveKeyId(); boolean isKeystoreEnabled(); + + KeyPair refreshKeyPairs(); } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java index b903767ff..a8c7db2f5 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtService.java @@ -1,6 +1,7 @@ package stirling.software.proprietary.security.service; import java.security.KeyPair; +import java.security.PublicKey; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -40,7 +41,7 @@ public class JwtService implements JwtServiceInterface { private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String BEARER_PREFIX = "Bearer "; private static final String ISSUER = "Stirling PDF"; - private static final long EXPIRATION = 3600000; + private static final long EXPIRATION = 300000; // 5 minutes in milliseconds private final JwtKeystoreServiceInterface keystoreService; private final boolean v2Enabled; @@ -71,7 +72,7 @@ public class JwtService implements JwtServiceInterface { @Override public String generateToken(String username, Map claims) { - KeyPair keyPair = keystoreService.getActiveKeypair(); + KeyPair keyPair = keystoreService.getActiveKeyPair(); var builder = Jwts.builder() @@ -92,7 +93,7 @@ public class JwtService implements JwtServiceInterface { @Override public void validateToken(String token) throws AuthenticationFailureException { - extractAllClaimsFromToken(token); + extractAllClaims(token); if (isTokenExpired(token)) { throw new AuthenticationFailureException("The token has expired"); @@ -105,8 +106,8 @@ public class JwtService implements JwtServiceInterface { } @Override - public Map extractAllClaims(String token) { - Claims claims = extractAllClaimsFromToken(token); + public Map extractClaims(String token) { + Claims claims = extractAllClaims(token); return new HashMap<>(claims); } @@ -120,29 +121,37 @@ public class JwtService implements JwtServiceInterface { } private T extractClaim(String token, Function claimsResolver) { - final Claims claims = extractAllClaimsFromToken(token); + final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } - private Claims extractAllClaimsFromToken(String token) { + private Claims extractAllClaims(String token) { try { // Extract key ID from token header if present - String keyId = extractKeyIdFromToken(token); + String keyId = extractKeyId(token); KeyPair keyPair; if (keyId != null) { - Optional specificKeyPair = keystoreService.getKeypairByKeyId(keyId); + log.debug("Looking up key pair for key ID: {}", keyId); + Optional specificKeyPair = keystoreService.getKeyPairByKeyId(keyId); + if (specificKeyPair.isPresent()) { keyPair = specificKeyPair.get(); + log.debug("Successfully found key pair for key ID: {}", keyId); } else { log.warn( "Key ID {} not found in keystore, token may have been signed with a rotated key", keyId); - throw new AuthenticationFailureException( - "JWT token signed with unknown key ID: " + keyId); + if (keystoreService.getActiveKeyId().equals(keyId)) { + log.debug("Rotating key pairs"); + keystoreService.refreshKeyPairs(); + } + + keyPair = keystoreService.getActiveKeyPair(); } } else { - keyPair = keystoreService.getActiveKeypair(); + log.debug("No key ID in token header, using active key pair"); + keyPair = keystoreService.getActiveKeyPair(); } return Jwts.parser() @@ -169,7 +178,7 @@ public class JwtService implements JwtServiceInterface { } @Override - public String extractTokenFromRequest(HttpServletRequest request) { + public String extractToken(HttpServletRequest request) { String authHeader = request.getHeader(AUTHORIZATION_HEADER); if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) { @@ -189,7 +198,7 @@ public class JwtService implements JwtServiceInterface { } @Override - public void addTokenToResponse(HttpServletResponse response, String token) { + public void addToken(HttpServletResponse response, String token) { response.setHeader(AUTHORIZATION_HEADER, Newlines.stripAll(BEARER_PREFIX + token)); ResponseCookie cookie = @@ -205,7 +214,7 @@ public class JwtService implements JwtServiceInterface { } @Override - public void clearTokenFromResponse(HttpServletResponse response) { + public void clearToken(HttpServletResponse response) { response.setHeader(AUTHORIZATION_HEADER, null); ResponseCookie cookie = @@ -225,17 +234,22 @@ public class JwtService implements JwtServiceInterface { return v2Enabled; } - private String extractKeyIdFromToken(String token) { + private String extractKeyId(String token) { try { - return (String) - Jwts.parser() - .unsecured() - .build() - .parseUnsecuredClaims(token) - .getHeader() - .get("kid"); + PublicKey signingKey = keystoreService.getActiveKeyPair().getPublic(); + + String keyId = + (String) + Jwts.parser() + .verifyWith(signingKey) + .build() + .parse(token) + .getHeader() + .get("kid"); + log.debug("Extracted key ID from token: {}", keyId); + return keyId; } catch (Exception e) { - log.debug("Failed to extract key ID from token header: {}", e.getMessage()); + log.warn("Failed to extract key ID from token header: {}", e.getMessage()); return null; } } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtServiceInterface.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtServiceInterface.java index 664d812d8..7cdca8209 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtServiceInterface.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtServiceInterface.java @@ -48,7 +48,7 @@ public interface JwtServiceInterface { * @param token the JWT token * @return map of claims */ - Map extractAllClaims(String token); + Map extractClaims(String token); /** * Check if token is expired @@ -64,7 +64,7 @@ public interface JwtServiceInterface { * @param request HTTP servlet request * @return JWT token if found, null otherwise */ - String extractTokenFromRequest(HttpServletRequest request); + String extractToken(HttpServletRequest request); /** * Add JWT token to HTTP response (header and cookie) @@ -72,14 +72,14 @@ public interface JwtServiceInterface { * @param response HTTP servlet response * @param token JWT token to add */ - void addTokenToResponse(HttpServletResponse response, String token); + void addToken(HttpServletResponse response, String token); /** * Clear JWT token from HTTP response (remove cookie) * * @param response HTTP servlet response */ - void clearTokenFromResponse(HttpServletResponse response); + void clearToken(HttpServletResponse response); /** * Check if JWT authentication is enabled 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 2ed4245f3..756405c20 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 @@ -33,8 +33,8 @@ class CustomLogoutSuccessHandlerTest { String logoutPath = "/login?logout=true"; when(response.isCommitted()).thenReturn(false); - when(jwtService.extractTokenFromRequest(request)).thenReturn(token); - doNothing().when(jwtService).clearTokenFromResponse(response); + when(jwtService.extractToken(request)).thenReturn(token); + doNothing().when(jwtService).clearToken(response); when(request.getContextPath()).thenReturn(""); when(response.encodeRedirectURL(logoutPath)).thenReturn(logoutPath); @@ -51,15 +51,15 @@ class CustomLogoutSuccessHandlerTest { String token = "token"; when(response.isCommitted()).thenReturn(false); - when(jwtService.extractTokenFromRequest(request)).thenReturn(token); - doNothing().when(jwtService).clearTokenFromResponse(response); + when(jwtService.extractToken(request)).thenReturn(token); + doNothing().when(jwtService).clearToken(response); when(request.getContextPath()).thenReturn(""); when(response.encodeRedirectURL(logoutPath)).thenReturn(logoutPath); customLogoutSuccessHandler.onLogoutSuccess(request, response, null); verify(response).sendRedirect(logoutPath); - verify(jwtService).clearTokenFromResponse(response); + verify(jwtService).clearToken(response); } @Test 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 7e67a9106..c42dd7405 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 @@ -81,7 +81,7 @@ class JwtAuthenticationFilterTest { jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); verify(filterChain).doFilter(request, response); - verify(jwtService, never()).extractTokenFromRequest(any()); + verify(jwtService, never()).extractToken(any()); } @Test @@ -105,9 +105,9 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getContextPath()).thenReturn("/"); when(request.getRequestURI()).thenReturn("/protected"); - when(jwtService.extractTokenFromRequest(request)).thenReturn(token); + when(jwtService.extractToken(request)).thenReturn(token); doNothing().when(jwtService).validateToken(token); - when(jwtService.extractAllClaims(token)).thenReturn(claims); + when(jwtService.extractClaims(token)).thenReturn(claims); when(userDetails.getAuthorities()).thenReturn(Collections.emptyList()); when(userDetailsService.loadUserByUsername(username)).thenReturn(userDetails); @@ -122,11 +122,11 @@ class JwtAuthenticationFilterTest { jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); verify(jwtService).validateToken(token); - verify(jwtService).extractAllClaims(token); + verify(jwtService).extractClaims(token); verify(userDetailsService).loadUserByUsername(username); verify(securityContext).setAuthentication(any(UsernamePasswordAuthenticationToken.class)); verify(jwtService).generateToken(any(UsernamePasswordAuthenticationToken.class), eq(claims)); - verify(jwtService).addTokenToResponse(response, newToken); + verify(jwtService).addToken(response, newToken); verify(filterChain).doFilter(request, response); } } @@ -136,7 +136,7 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/"); when(request.getMethod()).thenReturn("GET"); - when(jwtService.extractTokenFromRequest(request)).thenReturn(null); + when(jwtService.extractToken(request)).thenReturn(null); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); @@ -151,7 +151,7 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); when(request.getContextPath()).thenReturn("/"); - when(jwtService.extractTokenFromRequest(request)).thenReturn(token); + when(jwtService.extractToken(request)).thenReturn(token); doThrow(new AuthenticationFailureException("Invalid token")).when(jwtService).validateToken(token); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); @@ -168,7 +168,7 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); when(request.getContextPath()).thenReturn("/"); - when(jwtService.extractTokenFromRequest(request)).thenReturn(token); + when(jwtService.extractToken(request)).thenReturn(token); doThrow(new AuthenticationFailureException("The token has expired")).when(jwtService).validateToken(token); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); @@ -187,9 +187,9 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); when(request.getContextPath()).thenReturn("/"); - when(jwtService.extractTokenFromRequest(request)).thenReturn(token); + when(jwtService.extractToken(request)).thenReturn(token); doNothing().when(jwtService).validateToken(token); - when(jwtService.extractAllClaims(token)).thenReturn(claims); + when(jwtService.extractClaims(token)).thenReturn(claims); when(userDetailsService.loadUserByUsername(username)).thenReturn(null); try (MockedStatic mockedSecurityContextHolder = mockStatic(SecurityContextHolder.class)) { @@ -209,7 +209,7 @@ class JwtAuthenticationFilterTest { when(jwtService.isJwtEnabled()).thenReturn(true); when(request.getRequestURI()).thenReturn("/protected"); when(request.getContextPath()).thenReturn("/"); - when(jwtService.extractTokenFromRequest(request)).thenReturn(null); + when(jwtService.extractToken(request)).thenReturn(null); jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); 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 bce663aee..f08d33d8c 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 @@ -7,8 +7,6 @@ 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.junit.jupiter.params.provider.ValueSource; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockHttpServletRequest; @@ -105,7 +103,7 @@ class JwtSaml2AuthenticationRequestRepositoryTest { ); when(request.getParameter("RelayState")).thenReturn(relayState); - when(jwtService.extractAllClaims(token)).thenReturn(claims); + when(jwtService.extractClaims(token)).thenReturn(claims); when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")).thenReturn(relyingPartyRegistration); when(relyingPartyRegistration.getRegistrationId()).thenReturn("stirling-pdf"); when(relyingPartyRegistration.getAssertingPartyMetadata()).thenReturn(assertingPartyMetadata); @@ -153,7 +151,7 @@ class JwtSaml2AuthenticationRequestRepositoryTest { ); when(request.getParameter("RelayState")).thenReturn(relayState); - when(jwtService.extractAllClaims(token)).thenReturn(claims); + when(jwtService.extractClaims(token)).thenReturn(claims); when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")).thenReturn(null); tokenStore.put(relayState, token); @@ -179,7 +177,7 @@ class JwtSaml2AuthenticationRequestRepositoryTest { ); when(request.getParameter("RelayState")).thenReturn(relayState); - when(jwtService.extractAllClaims(token)).thenReturn(claims); + when(jwtService.extractClaims(token)).thenReturn(claims); when(relyingPartyRegistrationRepository.findByRegistrationId("stirling-pdf")).thenReturn(relyingPartyRegistration); when(relyingPartyRegistration.getRegistrationId()).thenReturn("stirling-pdf"); when(relyingPartyRegistration.getAssertingPartyMetadata()).thenReturn(assertingPartyMetadata); diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeyCleanupServiceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeyCleanupServiceTest.java index 2483fc69b..bdfc25947 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeyCleanupServiceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeyCleanupServiceTest.java @@ -70,7 +70,7 @@ class JwtKeyCleanupServiceTest { cleanupService.cleanup(); verify(signingKeyRepository, never()).countKeysEligibleForCleanup(any(LocalDateTime.class)); - verify(signingKeyRepository, never()).findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); + verify(signingKeyRepository, never()).findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); } @Test @@ -80,7 +80,7 @@ class JwtKeyCleanupServiceTest { cleanupService.cleanup(); verify(signingKeyRepository, never()).countKeysEligibleForCleanup(any(LocalDateTime.class)); - verify(signingKeyRepository, never()).findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); + verify(signingKeyRepository, never()).findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); } @Test @@ -90,7 +90,7 @@ class JwtKeyCleanupServiceTest { cleanupService.cleanup(); verify(signingKeyRepository).countKeysEligibleForCleanup(any(LocalDateTime.class)); - verify(signingKeyRepository, never()).findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); + verify(signingKeyRepository, never()).findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); } @Test @@ -106,14 +106,14 @@ class JwtKeyCleanupServiceTest { createTestKeyFile("key-2"); when(signingKeyRepository.countKeysEligibleForCleanup(any(LocalDateTime.class))).thenReturn(2L); - when(signingKeyRepository.findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class))) + when(signingKeyRepository.findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class))) .thenReturn(keysToCleanup) .thenReturn(Collections.emptyList()); cleanupService.cleanup(); verify(signingKeyRepository).countKeysEligibleForCleanup(any(LocalDateTime.class)); - verify(signingKeyRepository).findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); + verify(signingKeyRepository).findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); verify(signingKeyRepository).deleteAllByIdInBatch(Arrays.asList(1L, 2L)); assertFalse(Files.exists(tempDir.resolve("key-1.key"))); @@ -140,7 +140,7 @@ class JwtKeyCleanupServiceTest { createTestKeyFile("key-3"); when(signingKeyRepository.countKeysEligibleForCleanup(any(LocalDateTime.class))).thenReturn(3L); - when(signingKeyRepository.findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class))) + when(signingKeyRepository.findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class))) .thenReturn(firstBatch) .thenReturn(secondBatch) .thenReturn(Collections.emptyList()); @@ -165,7 +165,7 @@ class JwtKeyCleanupServiceTest { createTestKeyFile("key-1"); when(signingKeyRepository.countKeysEligibleForCleanup(any(LocalDateTime.class))).thenReturn(2L); - when(signingKeyRepository.findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class))) + when(signingKeyRepository.findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class))) .thenReturn(keysToCleanup) .thenReturn(Collections.emptyList()); @@ -226,7 +226,7 @@ class JwtKeyCleanupServiceTest { cleanupService.cleanup(); verify(signingKeyRepository, never()).countKeysEligibleForCleanup(any(LocalDateTime.class)); - verify(signingKeyRepository, never()).findInactiveKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); + verify(signingKeyRepository, never()).findKeysOlderThan(any(LocalDateTime.class), any(Pageable.class)); verify(signingKeyRepository, never()).deleteAllByIdInBatch(any()); } diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterfaceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterfaceTest.java index 595c4ebf8..8979bcbf6 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterfaceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterfaceTest.java @@ -77,14 +77,14 @@ class JwtKeystoreServiceInterfaceTest { } @Test - void testGetActiveKeypairWhenKeystoreDisabled() { + void testGetActiveKeyPairWhenKeystoreDisabled() { when(jwtConfig.isEnableKeystore()).thenReturn(false); try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); keystoreService = new JwtKeystoreService(repository, applicationProperties); - KeyPair result = keystoreService.getActiveKeypair(); + KeyPair result = keystoreService.getActiveKeyPair(); assertNotNull(result); assertNotNull(result.getPublic()); @@ -94,14 +94,14 @@ class JwtKeystoreServiceInterfaceTest { @Test void testGetActiveKeypairWhenNoActiveKeyExists() { - when(repository.findByIsActiveTrue()).thenReturn(Optional.empty()); + when(repository.findFirstByIsActiveTrueOrderByCreatedAtDesc()).thenReturn(Optional.empty()); try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); keystoreService = new JwtKeystoreService(repository, applicationProperties); keystoreService.initializeKeystore(); - KeyPair result = keystoreService.getActiveKeypair(); + KeyPair result = keystoreService.getActiveKeyPair(); assertNotNull(result); verify(repository).save(any(JwtSigningKey.class)); @@ -109,13 +109,13 @@ class JwtKeystoreServiceInterfaceTest { } @Test - void testGetActiveKeypairWithExistingKey() throws Exception { + 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()); JwtSigningKey existingKey = new JwtSigningKey(keyId, publicKeyBase64, "RS256"); - when(repository.findByIsActiveTrue()).thenReturn(Optional.of(existingKey)); + when(repository.findFirstByIsActiveTrueOrderByCreatedAtDesc()).thenReturn(Optional.of(existingKey)); Path keyFile = tempDir.resolve(keyId + ".key"); Files.writeString(keyFile, privateKeyBase64); @@ -125,7 +125,7 @@ class JwtKeystoreServiceInterfaceTest { keystoreService = new JwtKeystoreService(repository, applicationProperties); keystoreService.initializeKeystore(); - KeyPair result = keystoreService.getActiveKeypair(); + KeyPair result = keystoreService.getActiveKeyPair(); assertNotNull(result); assertEquals(keyId, keystoreService.getActiveKeyId()); @@ -133,7 +133,7 @@ class JwtKeystoreServiceInterfaceTest { } @Test - void testGetKeypairByKeyId() throws Exception { + void testGetKeyPairByKeyId() throws Exception { String keyId = "test-key-123"; String publicKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); String privateKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded()); @@ -148,7 +148,7 @@ class JwtKeystoreServiceInterfaceTest { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); keystoreService = new JwtKeystoreService(repository, applicationProperties); - Optional result = keystoreService.getKeypairByKeyId(keyId); + Optional result = keystoreService.getKeyPairByKeyId(keyId); assertTrue(result.isPresent()); assertNotNull(result.get().getPublic()); @@ -157,7 +157,7 @@ class JwtKeystoreServiceInterfaceTest { } @Test - void testGetKeypairByKeyIdNotFound() { + void testGetKeyPairByKeyIdNotFound() { String keyId = "non-existent-key"; when(repository.findByKeyId(keyId)).thenReturn(Optional.empty()); @@ -165,21 +165,21 @@ class JwtKeystoreServiceInterfaceTest { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); keystoreService = new JwtKeystoreService(repository, applicationProperties); - Optional result = keystoreService.getKeypairByKeyId(keyId); + Optional result = keystoreService.getKeyPairByKeyId(keyId); assertFalse(result.isPresent()); } } @Test - void testGetKeypairByKeyIdWhenKeystoreDisabled() { + void testGetKeyPairByKeyIdWhenKeystoreDisabled() { when(jwtConfig.isEnableKeystore()).thenReturn(false); try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); keystoreService = new JwtKeystoreService(repository, applicationProperties); - Optional result = keystoreService.getKeypairByKeyId("any-key"); + Optional result = keystoreService.getKeyPairByKeyId("any-key"); assertFalse(result.isPresent()); } @@ -187,7 +187,7 @@ class JwtKeystoreServiceInterfaceTest { @Test void testInitializeKeystoreCreatesDirectory() throws IOException { - when(repository.findByIsActiveTrue()).thenReturn(Optional.empty()); + when(repository.findFirstByIsActiveTrueOrderByCreatedAtDesc()).thenReturn(Optional.empty()); try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); @@ -205,14 +205,14 @@ class JwtKeystoreServiceInterfaceTest { String publicKeyBase64 = Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()); JwtSigningKey existingKey = new JwtSigningKey(keyId, publicKeyBase64, "RS256"); - when(repository.findByIsActiveTrue()).thenReturn(Optional.of(existingKey)); + when(repository.findFirstByIsActiveTrueOrderByCreatedAtDesc()).thenReturn(Optional.of(existingKey)); try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); keystoreService = new JwtKeystoreService(repository, applicationProperties); keystoreService.initializeKeystore(); - KeyPair result = keystoreService.getActiveKeypair(); + KeyPair result = keystoreService.getActiveKeyPair(); assertNotNull(result); verify(repository).save(any(JwtSigningKey.class)); 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 f5017a329..288dd2adb 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 @@ -74,7 +74,7 @@ class JwtServiceTest { void testGenerateTokenWithAuthentication() { String username = "testuser"; - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -93,7 +93,7 @@ class JwtServiceTest { claims.put("role", "admin"); claims.put("department", "IT"); - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -104,14 +104,14 @@ class JwtServiceTest { assertFalse(token.isEmpty()); assertEquals(username, jwtService.extractUsername(token)); - Map extractedClaims = jwtService.extractAllClaims(token); + Map extractedClaims = jwtService.extractClaims(token); assertEquals("admin", extractedClaims.get("role")); assertEquals("IT", extractedClaims.get("department")); } @Test void testValidateTokenSuccess() { - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn("testuser"); @@ -123,7 +123,7 @@ class JwtServiceTest { @Test void testValidateTokenWithInvalidToken() { - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); assertThrows(AuthenticationFailureException.class, () -> { jwtService.validateToken("invalid-token"); @@ -132,7 +132,7 @@ class JwtServiceTest { @Test void testValidateTokenWithMalformedToken() { - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); AuthenticationFailureException exception = assertThrows(AuthenticationFailureException.class, () -> { jwtService.validateToken("malformed.token"); @@ -143,7 +143,7 @@ class JwtServiceTest { @Test void testValidateTokenWithEmptyToken() { - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); AuthenticationFailureException exception = assertThrows(AuthenticationFailureException.class, () -> { jwtService.validateToken(""); @@ -158,7 +158,7 @@ class JwtServiceTest { User user = mock(User.class); Map claims = Map.of("sub", "testuser", "authType", "WEB"); - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(user); when(user.getUsername()).thenReturn(username); @@ -170,23 +170,23 @@ class JwtServiceTest { @Test void testExtractUsernameWithInvalidToken() { - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); assertThrows(AuthenticationFailureException.class, () -> jwtService.extractUsername("invalid-token")); } @Test - void testExtractAllClaims() { + void testExtractClaims() { String username = "testuser"; Map claims = Map.of("role", "admin", "department", "IT"); - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); String token = jwtService.generateToken(authentication, claims); - Map extractedClaims = jwtService.extractAllClaims(token); + Map extractedClaims = jwtService.extractClaims(token); assertEquals("admin", extractedClaims.get("role")); assertEquals("IT", extractedClaims.get("department")); @@ -195,60 +195,60 @@ class JwtServiceTest { } @Test - void testExtractAllClaimsWithInvalidToken() { - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + void testExtractClaimsWithInvalidToken() { + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); - assertThrows(AuthenticationFailureException.class, () -> jwtService.extractAllClaims("invalid-token")); + assertThrows(AuthenticationFailureException.class, () -> jwtService.extractClaims("invalid-token")); } @Test - void testExtractTokenFromRequestWithAuthorizationHeader() { + void testExtractTokenWithAuthorizationHeader() { String token = "test-token"; when(request.getHeader("Authorization")).thenReturn("Bearer " + token); - assertEquals(token, jwtService.extractTokenFromRequest(request)); + assertEquals(token, jwtService.extractToken(request)); } @Test - void testExtractTokenFromRequestWithCookie() { + void testExtractTokenWithCookie() { String token = "test-token"; Cookie[] cookies = { new Cookie("stirling_jwt", token) }; when(request.getHeader("Authorization")).thenReturn(null); when(request.getCookies()).thenReturn(cookies); - assertEquals(token, jwtService.extractTokenFromRequest(request)); + assertEquals(token, jwtService.extractToken(request)); } @Test - void testExtractTokenFromRequestWithNoCookies() { + void testExtractTokenWithNoCookies() { when(request.getHeader("Authorization")).thenReturn(null); when(request.getCookies()).thenReturn(null); - assertNull(jwtService.extractTokenFromRequest(request)); + assertNull(jwtService.extractToken(request)); } @Test - void testExtractTokenFromRequestWithWrongCookie() { + void testExtractTokenWithWrongCookie() { Cookie[] cookies = {new Cookie("OTHER_COOKIE", "value")}; when(request.getHeader("Authorization")).thenReturn(null); when(request.getCookies()).thenReturn(cookies); - assertNull(jwtService.extractTokenFromRequest(request)); + assertNull(jwtService.extractToken(request)); } @Test - void testExtractTokenFromRequestWithInvalidAuthorizationHeader() { + void testExtractTokenWithInvalidAuthorizationHeader() { when(request.getHeader("Authorization")).thenReturn("Basic token"); when(request.getCookies()).thenReturn(null); - assertNull(jwtService.extractTokenFromRequest(request)); + assertNull(jwtService.extractToken(request)); } @Test - void testAddTokenToResponse() { + void testAddToken() { String token = "test-token"; - jwtService.addTokenToResponse(response, token); + jwtService.addToken(response, token); verify(response).setHeader("Authorization", "Bearer " + token); verify(response).addHeader(eq("Set-Cookie"), contains("stirling_jwt=" + token)); @@ -257,8 +257,8 @@ class JwtServiceTest { } @Test - void testClearTokenFromResponse() { - jwtService.clearTokenFromResponse(response); + void testClearToken() { + jwtService.clearToken(response); verify(response).setHeader("Authorization", null); verify(response).addHeader(eq("Set-Cookie"), contains("stirling_jwt=")); @@ -270,7 +270,7 @@ class JwtServiceTest { String username = "testuser"; Map claims = new HashMap<>(); - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -280,7 +280,7 @@ class JwtServiceTest { assertNotNull(token); assertFalse(token.isEmpty()); // Verify that the keystore service was called - verify(keystoreService).getActiveKeypair(); + verify(keystoreService).getActiveKeyPair(); verify(keystoreService).getActiveKeyId(); } @@ -289,7 +289,7 @@ class JwtServiceTest { String username = "testuser"; Map claims = new HashMap<>(); - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -298,7 +298,7 @@ class JwtServiceTest { String token = jwtService.generateToken(authentication, claims); // Mock extraction of key ID and verification (lenient to avoid unused stubbing) - lenient().when(keystoreService.getKeypairByKeyId("test-key-id")).thenReturn(Optional.of(testKeyPair)); + lenient().when(keystoreService.getKeyPairByKeyId("test-key-id")).thenReturn(Optional.of(testKeyPair)); // Verify token can be validated assertDoesNotThrow(() -> jwtService.validateToken(token)); @@ -310,7 +310,7 @@ class JwtServiceTest { String username = "testuser"; Map claims = new HashMap<>(); - when(keystoreService.getActiveKeypair()).thenReturn(testKeyPair); + when(keystoreService.getActiveKeyPair()).thenReturn(testKeyPair); when(keystoreService.getActiveKeyId()).thenReturn("test-key-id"); when(authentication.getPrincipal()).thenReturn(userDetails); when(userDetails.getUsername()).thenReturn(username); @@ -318,13 +318,13 @@ class JwtServiceTest { String token = jwtService.generateToken(authentication, claims); // Mock scenario where specific key ID is not found (lenient to avoid unused stubbing) - lenient().when(keystoreService.getKeypairByKeyId("test-key-id")).thenReturn(Optional.empty()); + lenient().when(keystoreService.getKeyPairByKeyId("test-key-id")).thenReturn(Optional.empty()); // Should still work using active keypair assertDoesNotThrow(() -> jwtService.validateToken(token)); assertEquals(username, jwtService.extractUsername(token)); // Verify fallback to active keypair was used (called multiple times during token operations) - verify(keystoreService, atLeast(1)).getActiveKeypair(); + verify(keystoreService, atLeast(1)).getActiveKeyPair(); } }