diff --git a/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java b/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java index ebea350fc..dfe814d67 100644 --- a/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java +++ b/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java @@ -44,7 +44,7 @@ public class InstallationPathConfig { STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator; TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator; SIGNATURES_PATH = CUSTOM_FILES_PATH + "signatures" + File.separator; - PRIVATE_KEY_PATH = CUSTOM_FILES_PATH + "keys" + File.separator; + PRIVATE_KEY_PATH = CONFIG_PATH + "keys" + File.separator; } private static String initializeBasePath() { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java index ed309afda..7f2afc735 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java @@ -186,7 +186,7 @@ public class SecurityConfiguration { // Configure session management based on JWT setting http.sessionManagement( sessionManagement -> { - if (v2Enabled && !securityProperties.isSaml2Active()) { + if (v2Enabled) { sessionManagement.sessionCreationPolicy( SessionCreationPolicy.STATELESS); } else { 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 3d4b7006e..68bf44b3e 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 @@ -69,10 +69,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { } String jwtToken = jwtService.extractToken(request); - + // todo: X-API-KEY if (jwtToken == null) { // If they are unauthenticated and navigating to '/', redirect to '/login' instead of // sending a 401 + // todo: any unauthenticated requests should redirect to login if ("/".equals(request.getRequestURI()) && "GET".equalsIgnoreCase(request.getMethod())) { response.sendRedirect("/login"); 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 f035e1385..7e46c9b4f 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 @@ -43,13 +43,12 @@ public class JwtService implements JwtServiceInterface { private static final String ISSUER = "Stirling PDF"; private static final long EXPIRATION = 3600000; - private final JwtKeystoreServiceInterface keystoreService; + private final KeystoreServiceInterface keystoreService; private final boolean v2Enabled; @Autowired public JwtService( - @Qualifier("v2Enabled") boolean v2Enabled, - JwtKeystoreServiceInterface keystoreService) { + @Qualifier("v2Enabled") boolean v2Enabled, KeystoreServiceInterface keystoreService) { this.v2Enabled = v2Enabled; this.keystoreService = keystoreService; } @@ -132,7 +131,8 @@ public class JwtService implements JwtServiceInterface { if (keyId != null) { log.debug("Looking up key pair for key ID: {}", keyId); - Optional specificKeyPair = keystoreService.getKeyPairByKeyId(keyId); + Optional specificKeyPair = + keystoreService.getKeyPairByKeyId(keyId); // todo: move to in-memory cache if (specificKeyPair.isPresent()) { keyPair = specificKeyPair.get(); @@ -178,13 +178,8 @@ public class JwtService implements JwtServiceInterface { @Override public String extractToken(HttpServletRequest request) { - String authHeader = request.getHeader(AUTHORIZATION_HEADER); - - if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) { - return authHeader.substring(BEARER_PREFIX.length()); - } - Cookie[] cookies = request.getCookies(); + if (cookies != null) { for (Cookie cookie : cookies) { if (JWT_COOKIE_NAME.equals(cookie.getName())) { @@ -203,7 +198,7 @@ public class JwtService implements JwtServiceInterface { ResponseCookie cookie = ResponseCookie.from(JWT_COOKIE_NAME, Newlines.stripAll(token)) .httpOnly(true) - .secure(true) + // .secure(true) // todo: fix, make configurable .sameSite("Strict") .maxAge(EXPIRATION / 1000) .path("/") 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/KeyPairCleanupService.java similarity index 93% rename from app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeyCleanupService.java rename to app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPairCleanupService.java index 0f18c66e0..7727dc1bc 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/KeyPairCleanupService.java @@ -17,6 +17,8 @@ import org.springframework.scheduling.annotation.Scheduled; 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; @@ -27,16 +29,16 @@ import stirling.software.proprietary.security.model.JwtSigningKey; @Slf4j @Service @ConditionalOnBooleanProperty("v2") -public class JwtKeyCleanupService { +public class KeyPairCleanupService { private final JwtSigningKeyRepository signingKeyRepository; - private final JwtKeystoreService keystoreService; + private final KeystoreService keystoreService; private final ApplicationProperties.Security.Jwt jwtProperties; @Autowired - public JwtKeyCleanupService( + public KeyPairCleanupService( JwtSigningKeyRepository signingKeyRepository, - JwtKeystoreService keystoreService, + KeystoreService keystoreService, ApplicationProperties applicationProperties) { this.signingKeyRepository = signingKeyRepository; this.keystoreService = keystoreService; @@ -44,6 +46,7 @@ public class JwtKeyCleanupService { } @Transactional + @PostConstruct @Scheduled(fixedDelay = 24, timeUnit = TimeUnit.HOURS) public void cleanup() { if (!jwtProperties.isEnableKeyCleanup() || !keystoreService.isKeystoreEnabled()) { @@ -113,7 +116,7 @@ public class JwtKeyCleanupService { } Path privateKeyDirectory = Paths.get(InstallationPathConfig.getPrivateKeyPath()); - Path keyFile = privateKeyDirectory.resolve(keyId + JwtKeystoreService.KEY_SUFFIX); + Path keyFile = privateKeyDirectory.resolve(keyId + KeystoreService.KEY_SUFFIX); if (Files.exists(keyFile)) { Files.delete(keyFile); 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/KeystoreService.java similarity index 96% rename from app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreService.java rename to app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeystoreService.java index 4155ce5cb..3cb73aadf 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/KeystoreService.java @@ -33,7 +33,7 @@ import stirling.software.proprietary.security.model.JwtSigningKey; @Slf4j @Service -public class JwtKeystoreService implements JwtKeystoreServiceInterface { +public class KeystoreService implements KeystoreServiceInterface { public static final String KEY_SUFFIX = ".key"; private final JwtSigningKeyRepository signingKeyRepository; @@ -43,7 +43,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { private volatile String currentKeyId; @Autowired - public JwtKeystoreService( + public KeystoreService( JwtSigningKeyRepository signingKeyRepository, ApplicationProperties applicationProperties) { this.signingKeyRepository = signingKeyRepository; @@ -53,7 +53,6 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { @PostConstruct public void initializeKeystore() { if (!isKeystoreEnabled()) { - log.info("Keystore is disabled, using in-memory key generation"); return; } @@ -61,7 +60,7 @@ public class JwtKeystoreService implements JwtKeystoreServiceInterface { ensurePrivateKeyDirectoryExists(); loadOrGenerateKeypair(); } catch (Exception e) { - log.error("Failed to initialize keystore, falling back to in-memory generation", e); + log.error("Failed to initialize keystore, using in-memory generation", e); } } 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/KeystoreServiceInterface.java similarity index 86% rename from app/proprietary/src/main/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterface.java rename to app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeystoreServiceInterface.java index 4cf9d8f55..dc0564980 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/KeystoreServiceInterface.java @@ -3,7 +3,7 @@ package stirling.software.proprietary.security.service; import java.security.KeyPair; import java.util.Optional; -public interface JwtKeystoreServiceInterface { +public interface KeystoreServiceInterface { KeyPair getActiveKeyPair(); 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 288dd2adb..6c7e2770a 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 @@ -55,7 +55,7 @@ class JwtServiceTest { private HttpServletResponse response; @Mock - private JwtKeystoreServiceInterface keystoreService; + private KeystoreServiceInterface keystoreService; private JwtService jwtService; private KeyPair testKeyPair; 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/KeyPairCleanupServiceTest.java similarity index 97% rename from app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeyCleanupServiceTest.java rename to app/proprietary/src/test/java/stirling/software/proprietary/security/service/KeyPairCleanupServiceTest.java index bdfc25947..ae07362c8 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/KeyPairCleanupServiceTest.java @@ -27,13 +27,13 @@ import stirling.software.proprietary.security.database.repository.JwtSigningKeyR import stirling.software.proprietary.security.model.JwtSigningKey; @ExtendWith(MockitoExtension.class) -class JwtKeyCleanupServiceTest { +class KeyPairCleanupServiceTest { @Mock private JwtSigningKeyRepository signingKeyRepository; @Mock - private JwtKeystoreService keystoreService; + private KeystoreService keystoreService; @Mock private ApplicationProperties applicationProperties; @@ -47,7 +47,7 @@ class JwtKeyCleanupServiceTest { @TempDir private Path tempDir; - private JwtKeyCleanupService cleanupService; + private KeyPairCleanupService cleanupService; @BeforeEach void setUp() { @@ -59,7 +59,7 @@ class JwtKeyCleanupServiceTest { lenient().when(jwtConfig.getCleanupBatchSize()).thenReturn(100); lenient().when(keystoreService.isKeystoreEnabled()).thenReturn(true); - cleanupService = new JwtKeyCleanupService(signingKeyRepository, keystoreService, applicationProperties); + cleanupService = new KeyPairCleanupService(signingKeyRepository, keystoreService, applicationProperties); } @@ -101,7 +101,7 @@ class JwtKeyCleanupServiceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - + createTestKeyFile("key-1"); createTestKeyFile("key-2"); @@ -134,7 +134,7 @@ class JwtKeyCleanupServiceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - + createTestKeyFile("key-1"); createTestKeyFile("key-2"); createTestKeyFile("key-3"); @@ -161,7 +161,7 @@ class JwtKeyCleanupServiceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - + createTestKeyFile("key-1"); when(signingKeyRepository.countKeysEligibleForCleanup(any(LocalDateTime.class))).thenReturn(2L); 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/KeystoreServiceInterfaceTest.java similarity index 90% rename from app/proprietary/src/test/java/stirling/software/proprietary/security/service/JwtKeystoreServiceInterfaceTest.java rename to app/proprietary/src/test/java/stirling/software/proprietary/security/service/KeystoreServiceInterfaceTest.java index 8979bcbf6..2380eb00b 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/KeystoreServiceInterfaceTest.java @@ -32,7 +32,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -class JwtKeystoreServiceInterfaceTest { +class KeystoreServiceInterfaceTest { @Mock private JwtSigningKeyRepository repository; @@ -49,7 +49,7 @@ class JwtKeystoreServiceInterfaceTest { @TempDir Path tempDir; - private JwtKeystoreService keystoreService; + private KeystoreService keystoreService; private KeyPair testKeyPair; @BeforeEach @@ -70,7 +70,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); assertEquals(keystoreEnabled, keystoreService.isKeystoreEnabled()); } @@ -82,7 +82,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); KeyPair result = keystoreService.getActiveKeyPair(); @@ -98,7 +98,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); keystoreService.initializeKeystore(); KeyPair result = keystoreService.getActiveKeyPair(); @@ -122,7 +122,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); keystoreService.initializeKeystore(); KeyPair result = keystoreService.getActiveKeyPair(); @@ -146,7 +146,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); Optional result = keystoreService.getKeyPairByKeyId(keyId); @@ -163,7 +163,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); Optional result = keystoreService.getKeyPairByKeyId(keyId); @@ -177,7 +177,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); Optional result = keystoreService.getKeyPairByKeyId("any-key"); @@ -191,7 +191,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); keystoreService.initializeKeystore(); assertTrue(Files.exists(tempDir)); @@ -209,7 +209,7 @@ class JwtKeystoreServiceInterfaceTest { try (MockedStatic mockedStatic = mockStatic(InstallationPathConfig.class)) { mockedStatic.when(InstallationPathConfig::getPrivateKeyPath).thenReturn(tempDir.toString()); - keystoreService = new JwtKeystoreService(repository, applicationProperties); + keystoreService = new KeystoreService(repository, applicationProperties); keystoreService.initializeKeystore(); KeyPair result = keystoreService.getActiveKeyPair();