wip - configuring resource server

This commit is contained in:
Dario Ghunney Ware 2025-08-14 18:59:16 +01:00 committed by DarioGii
parent 0b224c8547
commit 41dfb1c4f6
18 changed files with 211 additions and 232 deletions

2
.gitignore vendored
View File

@ -18,6 +18,7 @@ version.properties
#### Stirling-PDF Files ###
pipeline/watchedFolders/
pipeline/defaultWebUIConfigs/
pipeline/finishedFolders/
customFiles/
configs/
@ -200,3 +201,4 @@ id_ed25519.pub
# node_modules
node_modules/
app/core/src/main/resources/application-saas.yml

View File

@ -372,7 +372,7 @@ public class ApplicationProperties {
public String getBaseTmpDir() {
return baseTmpDir != null && !baseTmpDir.isEmpty()
? baseTmpDir
: java.lang.System.getProperty("java.io.tmpdir") + "/stirling-pdf";
: java.lang.System.getProperty("java.io.tmpdir") + "stirling-pdf";
}
@JsonIgnore

View File

@ -1,72 +0,0 @@
spring:
main.allow-bean-definition-overriding: true
web.resources.mime-mappings.webmanifest: application/manifest+json
mvc.async.request-timeout: ${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
thymeleaf.encoding: UTF-8
jpa.open-in-view: false
servlet.multipart:
max-file-size: 2000MB
max-request-size: 2000MB
devtools:
restart:
enabled: true
exclude: stirling.software.proprietary.security/**
livereload.enabled: true
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://db.nrlkjfznsavsbmweiyqu.supabase.co:5432/postgres?sslmode=require
username: postgres
password: ${SAAS_DB_PASSWORD}
jpa:
hibernate.ddl-auto: update
database-platform: org.hibernate.dialect.PostgreSQLDialect
security:
oauth2:
res
multipart.enabled: true
logging.level:
org:
springframework: WARN
hibernate: WARN
jetty: WARN
# springframework.security.saml2: TRACE
# springframework.security: DEBUG
# opensaml: DEBUG
# stirling.software.SPDF.config.security: DEBUG
com.zaxxer.hikari: WARN
server:
forward-headers-strategy: NATIVE
error:
path: /error
whitelabel.enabled: false
include-stacktrace: always
include-exception: true
include-message: always
servlet:
session:
timeout: 30m
tracking-modes: cookie
context-path: ${SYSTEM_ROOTURIPATH:/}
# Change the default URL path for OpenAPI JSON
springdoc:
api-docs.path: /v1/api-docs
swagger-ui:
url: /v1/api-docs
path: /index.html
posthog:
api:
key: ${POSTHOG_API_KEY:phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq}
host: ${POSTHOG_HOST:https://eu.i.posthog.com}
supabase:
id: ${SUPABASE_PROJECT_ID:nrlkjfznsavsbmweiyqu}
publishable-key: ${SUPABASE_PUBLISHABLE_KEY:rp7EBq9Dk-ZPCSe7EMWD7JZ6rMulReSUmJ4WIHowMl1AlCC6m1xA3UsBctHRbFkttik9pReYltrWzt7Ft1aap36_S2tKRzEN9qngJM1D7yd2s0Ok0kLeC54DxBvuqQVKe4dk6g_XC7ElV8w6JUQBN9xMLbnMQG49qvq2syugk-Ujj1M9VGsNr85HdgAFymODW3vI4w1hrz4rCDOU_uuDDGHoEvChnFVZ_tmO80IUKUCiWIIzkBn8k8mnmbnC0vCRMV-YT1J7DS-pznuqcEZhotJ3DnD3TwNJevVklo7QfZGL6hyIK-t5DNMBc3uujS1bZ9bLA3RMqXWgD9ZTmjcutw}
jwks:
url: https://nrlkjfznsavsbmweiyqu.supabase.co/auth/v1/.well-known/jwks.json
# Set up a consistent temporary directory location
java.io.tmpdir: ${stirling.tempfiles.directory:${java.io.tmpdir}/stirling-pdf}

View File

@ -40,8 +40,11 @@ dependencies {
api 'org.springframework:spring-jdbc'
api 'org.springframework:spring-webmvc'
api 'org.springframework.session:spring-session-core'
api "org.springframework.security:spring-security-core:$springSecuritySamlVersion"
api "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
api "org.springframework.security:spring-security-core:$springSecurityVersion"
api "org.springframework.security:spring-security-saml2-service-provider:$springSecurityVersion"
api "org.springframework.security:spring-security-oauth2-resource-server:$springSecurityVersion"
api "org.springframework.security:spring-security-oauth2-jose:$springSecurityVersion"
api 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.6.8'
api 'org.springframework.boot:spring-boot-starter-jetty'
api 'org.springframework.boot:spring-boot-starter-security'
api 'org.springframework.boot:spring-boot-starter-data-jpa'

View File

@ -0,0 +1,38 @@
package stirling.software.proprietary.controller;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MeController {
@GetMapping("/me")
public Map<String, Object> me(@AuthenticationPrincipal Jwt jwt, Authentication authentication) {
List<String> authorities =
authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.toList();
return Map.of(
"subject", jwt.getSubject(),
"issuer", jwt.getIssuer() != null ? jwt.getIssuer().toString() : null,
"audience", jwt.getAudience(),
"issuedAt", toEpoch(jwt.getIssuedAt()),
"expiresAt", toEpoch(jwt.getExpiresAt()),
"authorities", authorities,
"claims", jwt.getClaims() // full map of claims from the token
);
}
private Long toEpoch(Instant i) {
return i == null ? null : i.getEpochSecond();
}
}

View File

@ -1,8 +1,8 @@
package stirling.software.proprietary.security;
import java.io.IOException;
import java.util.Map;
import java.util.Map;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.SavedRequest;
@ -53,10 +53,11 @@ public class CustomAuthenticationSuccessHandler
}
loginAttemptService.loginSucceeded(userName);
if (jwtService.isJwtEnabled()) {
if (true) {
String jwt =
jwtService.generateToken(
authentication, Map.of("authType", AuthenticationType.WEB));
authentication, Map.of("authType",
AuthenticationType.WEB));
jwtService.addToken(response, jwt);
log.debug("JWT generated for user: {}", userName);

View File

@ -7,9 +7,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProp
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import lombok.Getter;
@ -58,18 +56,18 @@ public class DatabaseConfig {
* @return a <code>DataSource</code> using the configuration settings in the settings.yml
* @throws UnsupportedProviderException if the type of database selected is not supported
*/
@Bean
@Qualifier("dataSource")
@Primary
public DataSource dataSource() throws UnsupportedProviderException {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
if (!runningProOrHigher || !datasource.isEnableCustomDatabase()) {
return useDefaultDataSource(dataSourceBuilder);
}
return useCustomDataSource(dataSourceBuilder);
}
// @Bean
// @Qualifier("dataSource")
// @Primary
// public DataSource dataSource() throws UnsupportedProviderException {
// DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
//
// if (!runningProOrHigher || !datasource.isEnableCustomDatabase()) {
// return useDefaultDataSource(dataSourceBuilder);
// }
//
// return useCustomDataSource(dataSourceBuilder);
// }
private DataSource useDefaultDataSource(DataSourceBuilder<?> dataSourceBuilder) {
log.info("Using default H2 database");

View File

@ -18,6 +18,7 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
@ -132,20 +133,24 @@ public class SecurityConfiguration {
if (loginEnabledValue) {
boolean v2Enabled = appConfig.v2Enabled();
if (v2Enabled) {
http.addFilterBefore(
jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(
exceptionHandling ->
exceptionHandling.authenticationEntryPoint(
jwtAuthenticationEntryPoint));
}
http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(rateLimitingFilter(), UserAuthenticationFilter.class)
.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
// if (v2Enabled) {
http.oauth2ResourceServer(
oauth2 ->
oauth2.jwt(
jwtConfigurer ->
jwtConfigurer.jwkSetUri(
"https://nrlkjfznsavsbmweiyqu.supabase.co/auth/v1/.well-known/jwks.json")));
http.addFilterBefore(jwtAuthenticationFilter(), BearerTokenAuthenticationFilter.class)
.exceptionHandling(
exceptionHandling ->
exceptionHandling.authenticationEntryPoint(
jwtAuthenticationEntryPoint));
// }
if (!securityProperties.getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo =
CookieCsrfTokenRepository.withHttpOnlyFalse();

View File

@ -111,7 +111,9 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
String contextPath = request.getContextPath();
if ("GET".equalsIgnoreCase(method) && !requestURI.startsWith(contextPath + "/login")) {
response.sendRedirect(contextPath + "/login"); // redirect to the login page
// response.sendRedirect(contextPath + "/login"); // redirect to the
// login page
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter()

View File

@ -4,6 +4,9 @@ import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
@ -15,19 +18,27 @@ import lombok.ToString;
@NoArgsConstructor
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class JwtVerificationKey implements Serializable {
@JsonIgnoreProperties(ignoreUnknown = true)
public class JwtSigningKey implements Serializable {
@Serial private static final long serialVersionUID = 1L;
@ToString.Include private String keyId;
@JsonAlias("kid")
@ToString.Include
private String keyId;
private String verifyingKey;
@JsonAlias("n")
private String key;
@JsonAlias("alg")
private String algorithm;
@ToString.Include private LocalDateTime createdAt;
public JwtVerificationKey(String keyId, String verifyingKey) {
public JwtSigningKey(String keyId, String key) {
this.keyId = keyId;
this.verifyingKey = verifyingKey;
this.key = key;
this.algorithm = "RS256";
this.createdAt = LocalDateTime.now();
}
}

View File

@ -35,7 +35,7 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.proprietary.security.model.JwtVerificationKey;
import stirling.software.proprietary.security.model.JwtSigningKey;
import stirling.software.proprietary.security.model.exception.AuthenticationFailureException;
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
@ -80,7 +80,7 @@ public class JwtService implements JwtServiceInterface {
@Override
public String generateToken(String username, Map<String, Object> claims) {
try {
JwtVerificationKey activeKey = keyPersistenceService.getActiveKey();
JwtSigningKey activeKey = keyPersistenceService.getActiveKey();
Optional<KeyPair> keyPairOpt = keyPersistenceService.getKeyPair(activeKey.getKeyId());
if (keyPairOpt.isEmpty()) {
@ -159,7 +159,7 @@ public class JwtService implements JwtServiceInterface {
keyId);
if (keyId.equals(keyPersistenceService.getActiveKey().getKeyId())) {
JwtVerificationKey verificationKey =
JwtSigningKey verificationKey =
keyPersistenceService.refreshActiveKeyPair();
Optional<KeyPair> refreshedKeyPair =
keyPersistenceService.getKeyPair(verificationKey.getKeyId());
@ -171,7 +171,7 @@ public class JwtService implements JwtServiceInterface {
}
} else {
// Try to use active key as fallback
JwtVerificationKey activeKey = keyPersistenceService.getActiveKey();
JwtSigningKey activeKey = keyPersistenceService.getActiveKey();
Optional<KeyPair> activeKeyPair =
keyPersistenceService.getKeyPair(activeKey.getKeyId());
if (activeKeyPair.isPresent()) {
@ -214,9 +214,8 @@ public class JwtService implements JwtServiceInterface {
private Claims tryAllKeys(String token) throws AuthenticationFailureException {
// First try the active key
try {
JwtVerificationKey activeKey = keyPersistenceService.getActiveKey();
PublicKey publicKey =
keyPersistenceService.decodePublicKey(activeKey.getVerifyingKey());
JwtSigningKey activeKey = keyPersistenceService.getActiveKey();
PublicKey publicKey = keyPersistenceService.decodePublicKey(activeKey.getKey());
return Jwts.parser()
.verifyWith(publicKey)
.build()
@ -228,15 +227,14 @@ public class JwtService implements JwtServiceInterface {
log.debug("Active key failed, trying all available keys from cache");
// If active key fails, try all available keys from cache
List<JwtVerificationKey> allKeys =
List<JwtSigningKey> allKeys =
keyPersistenceService.getKeysEligibleForCleanup(
LocalDateTime.now().plusDays(1));
for (JwtVerificationKey verificationKey : allKeys) {
for (JwtSigningKey verificationKey : allKeys) {
try {
PublicKey publicKey =
keyPersistenceService.decodePublicKey(
verificationKey.getVerifyingKey());
keyPersistenceService.decodePublicKey(verificationKey.getKey());
return Jwts.parser()
.verifyWith(publicKey)
.build()
@ -310,7 +308,7 @@ public class JwtService implements JwtServiceInterface {
try {
PublicKey signingKey =
keyPersistenceService.decodePublicKey(
keyPersistenceService.getActiveKey().getVerifyingKey());
keyPersistenceService.getActiveKey().getKey());
String keyId =
(String)

View File

@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.proprietary.security.model.JwtVerificationKey;
import stirling.software.proprietary.security.model.JwtSigningKey;
@Slf4j
@Service
@ -49,7 +49,7 @@ public class KeyPairCleanupService {
LocalDateTime cutoffDate =
LocalDateTime.now().minusDays(jwtProperties.getKeyRetentionDays());
List<JwtVerificationKey> eligibleKeys =
List<JwtSigningKey> eligibleKeys =
keyPersistenceService.getKeysEligibleForCleanup(cutoffDate);
if (eligibleKeys.isEmpty()) {
return;
@ -60,7 +60,7 @@ public class KeyPairCleanupService {
keyPersistenceService.refreshActiveKeyPair();
}
private void removeKeys(List<JwtVerificationKey> keys) {
private void removeKeys(List<JwtSigningKey> keys) {
keys.forEach(
key -> {
try {

View File

@ -34,7 +34,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.proprietary.security.model.JwtVerificationKey;
import stirling.software.proprietary.security.model.JwtSigningKey;
@Slf4j
@Service
@ -46,7 +46,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
private final CacheManager cacheManager;
private final Cache verifyingKeyCache;
private volatile JwtVerificationKey activeKey;
private volatile JwtSigningKey activeKey;
@Autowired
public KeyPersistenceService(
@ -77,15 +77,15 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
}
@Transactional
private JwtVerificationKey generateAndStoreKeypair() {
JwtVerificationKey verifyingKey = null;
private JwtSigningKey generateAndStoreKeypair() {
JwtSigningKey verifyingKey = null;
try {
KeyPair keyPair = generateRSAKeypair();
String keyId = generateKeyId();
storePrivateKey(keyId, keyPair.getPrivate());
verifyingKey = new JwtVerificationKey(keyId, encodePublicKey(keyPair.getPublic()));
verifyingKey = new JwtSigningKey(keyId, encodePublicKey(keyPair.getPublic()));
verifyingKeyCache.put(keyId, verifyingKey);
activeKey = verifyingKey;
} catch (IOException e) {
@ -96,7 +96,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
}
@Override
public JwtVerificationKey getActiveKey() {
public JwtSigningKey getActiveKey() {
if (activeKey == null) {
return generateAndStoreKeypair();
}
@ -110,8 +110,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
}
try {
JwtVerificationKey verifyingKey =
verifyingKeyCache.get(keyId, JwtVerificationKey.class);
JwtSigningKey verifyingKey = verifyingKeyCache.get(keyId, JwtSigningKey.class);
if (verifyingKey == null) {
log.warn("No signing key found in database for keyId: {}", keyId);
@ -119,7 +118,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
}
PrivateKey privateKey = loadPrivateKey(keyId);
PublicKey publicKey = decodePublicKey(verifyingKey.getVerifyingKey());
PublicKey publicKey = decodePublicKey(verifyingKey.getKey());
return Optional.of(new KeyPair(publicKey, privateKey));
} catch (Exception e) {
@ -134,7 +133,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
}
@Override
public JwtVerificationKey refreshActiveKeyPair() {
public JwtSigningKey refreshActiveKeyPair() {
return generateAndStoreKeypair();
}
@ -148,7 +147,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
}
@Override
public List<JwtVerificationKey> getKeysEligibleForCleanup(LocalDateTime cutoffDate) {
public List<JwtSigningKey> getKeysEligibleForCleanup(LocalDateTime cutoffDate) {
CaffeineCache caffeineCache = (CaffeineCache) verifyingKeyCache;
com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache =
caffeineCache.getNativeCache();
@ -159,8 +158,8 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
nativeCache.asMap().size());
return nativeCache.asMap().values().stream()
.filter(value -> value instanceof JwtVerificationKey)
.map(value -> (JwtVerificationKey) value)
.filter(value -> value instanceof JwtSigningKey)
.map(value -> (JwtSigningKey) value)
.filter(
key -> {
boolean eligible = key.getCreatedAt().isBefore(cutoffDate);

View File

@ -8,19 +8,19 @@ import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import stirling.software.proprietary.security.model.JwtVerificationKey;
import stirling.software.proprietary.security.model.JwtSigningKey;
public interface KeyPersistenceServiceInterface {
JwtVerificationKey getActiveKey();
JwtSigningKey getActiveKey();
Optional<KeyPair> getKeyPair(String keyId);
boolean isKeystoreEnabled();
JwtVerificationKey refreshActiveKeyPair();
JwtSigningKey refreshActiveKeyPair();
List<JwtVerificationKey> getKeysEligibleForCleanup(LocalDateTime cutoffDate);
List<JwtSigningKey> getKeysEligibleForCleanup(LocalDateTime cutoffDate);
void removeKey(String keyId);

View File

@ -1,20 +1,13 @@
package stirling.software.proprietary.security.configuration;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import javax.sql.DataSource;
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 stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.exception.UnsupportedProviderException;
@ExtendWith(MockitoExtension.class)
class DatabaseConfigTest {
@ -28,59 +21,60 @@ class DatabaseConfigTest {
databaseConfig = new DatabaseConfig(datasource, true);
}
@Test
void testDataSource_whenRunningEEIsFalse() throws UnsupportedProviderException {
databaseConfig = new DatabaseConfig(datasource, false);
var result = databaseConfig.dataSource();
assertInstanceOf(DataSource.class, result);
}
@Test
void testDefaultConfigurationForDataSource() throws UnsupportedProviderException {
when(datasource.isEnableCustomDatabase()).thenReturn(false);
var result = databaseConfig.dataSource();
assertInstanceOf(DataSource.class, result);
}
@Test
void testCustomUrlForDataSource() throws UnsupportedProviderException {
when(datasource.isEnableCustomDatabase()).thenReturn(true);
when(datasource.getCustomDatabaseUrl()).thenReturn("jdbc:postgresql://mockUrl");
when(datasource.getUsername()).thenReturn("test");
when(datasource.getPassword()).thenReturn("pass");
var result = databaseConfig.dataSource();
assertInstanceOf(DataSource.class, result);
}
@Test
void testCustomConfigurationForDataSource() throws UnsupportedProviderException {
when(datasource.isEnableCustomDatabase()).thenReturn(true);
when(datasource.getCustomDatabaseUrl()).thenReturn("");
when(datasource.getType()).thenReturn("postgresql");
when(datasource.getHostName()).thenReturn("test");
when(datasource.getPort()).thenReturn(1234);
when(datasource.getName()).thenReturn("test_db");
when(datasource.getUsername()).thenReturn("test");
when(datasource.getPassword()).thenReturn("pass");
var result = databaseConfig.dataSource();
assertInstanceOf(DataSource.class, result);
}
@ParameterizedTest(name = "Exception thrown when the DB type [{arguments}] is not supported")
@ValueSource(strings = {"oracle", "mysql", "mongoDb"})
void exceptionThrown_whenDBTypeIsUnsupported(String datasourceType) {
when(datasource.isEnableCustomDatabase()).thenReturn(true);
when(datasource.getCustomDatabaseUrl()).thenReturn("");
when(datasource.getType()).thenReturn(datasourceType);
assertThrows(UnsupportedProviderException.class, () -> databaseConfig.dataSource());
}
// @Test
// void testDataSource_whenRunningEEIsFalse() throws UnsupportedProviderException {
// databaseConfig = new DatabaseConfig(datasource, false);
//
// var result = databaseConfig.dataSource();
//
// assertInstanceOf(DataSource.class, result);
// }
//
// @Test
// void testDefaultConfigurationForDataSource() throws UnsupportedProviderException {
// when(datasource.isEnableCustomDatabase()).thenReturn(false);
//
// var result = databaseConfig.dataSource();
//
// assertInstanceOf(DataSource.class, result);
// }
//
// @Test
// void testCustomUrlForDataSource() throws UnsupportedProviderException {
// when(datasource.isEnableCustomDatabase()).thenReturn(true);
// when(datasource.getCustomDatabaseUrl()).thenReturn("jdbc:postgresql://mockUrl");
// when(datasource.getUsername()).thenReturn("test");
// when(datasource.getPassword()).thenReturn("pass");
//
// var result = databaseConfig.dataSource();
//
// assertInstanceOf(DataSource.class, result);
// }
//
// @Test
// void testCustomConfigurationForDataSource() throws UnsupportedProviderException {
// when(datasource.isEnableCustomDatabase()).thenReturn(true);
// when(datasource.getCustomDatabaseUrl()).thenReturn("");
// when(datasource.getType()).thenReturn("postgresql");
// when(datasource.getHostName()).thenReturn("test");
// when(datasource.getPort()).thenReturn(1234);
// when(datasource.getName()).thenReturn("test_db");
// when(datasource.getUsername()).thenReturn("test");
// when(datasource.getPassword()).thenReturn("pass");
//
// var result = databaseConfig.dataSource();
//
// assertInstanceOf(DataSource.class, result);
// }
//
// @ParameterizedTest(name = "Exception thrown when the DB type [{arguments}] is not
// supported")
// @ValueSource(strings = {"oracle", "mysql", "mongoDb"})
// void exceptionThrown_whenDBTypeIsUnsupported(String datasourceType) {
// when(datasource.isEnableCustomDatabase()).thenReturn(true);
// when(datasource.getCustomDatabaseUrl()).thenReturn("");
// when(datasource.getType()).thenReturn(datasourceType);
//
// assertThrows(UnsupportedProviderException.class, () -> databaseConfig.dataSource());
// }
}

View File

@ -37,7 +37,7 @@ 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.JwtSigningKey;
import stirling.software.proprietary.security.model.User;
import stirling.software.proprietary.security.model.exception.AuthenticationFailureException;
@ -56,7 +56,7 @@ class JwtServiceTest {
private JwtService jwtService;
private KeyPair testKeyPair;
private JwtVerificationKey testVerificationKey;
private JwtSigningKey testVerificationKey;
@BeforeEach
void setUp() throws NoSuchAlgorithmException {
@ -68,7 +68,7 @@ class JwtServiceTest {
// Create test verification key
String encodedPublicKey =
Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded());
testVerificationKey = new JwtVerificationKey("test-key-id", encodedPublicKey);
testVerificationKey = new JwtSigningKey("test-key-id", encodedPublicKey);
jwtService = new JwtService(true, keystoreService);
}
@ -79,7 +79,7 @@ class JwtServiceTest {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair));
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
when(authentication.getPrincipal()).thenReturn(userDetails);
when(userDetails.getUsername()).thenReturn(username);
@ -100,7 +100,7 @@ class JwtServiceTest {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair));
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
when(authentication.getPrincipal()).thenReturn(userDetails);
when(userDetails.getUsername()).thenReturn(username);
@ -120,7 +120,7 @@ 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()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
when(authentication.getPrincipal()).thenReturn(userDetails);
when(userDetails.getUsername()).thenReturn("testuser");
@ -133,7 +133,7 @@ class JwtServiceTest {
@Test
void testValidateTokenWithInvalidToken() throws Exception {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
assertThrows(
@ -146,7 +146,7 @@ class JwtServiceTest {
@Test
void testValidateTokenWithMalformedToken() throws Exception {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
AuthenticationFailureException exception =
@ -162,7 +162,7 @@ class JwtServiceTest {
@Test
void testValidateTokenWithEmptyToken() throws Exception {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
AuthenticationFailureException exception =
@ -185,7 +185,7 @@ class JwtServiceTest {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair));
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
when(authentication.getPrincipal()).thenReturn(user);
when(user.getUsername()).thenReturn(username);
@ -198,7 +198,7 @@ class JwtServiceTest {
@Test
void testExtractUsernameWithInvalidToken() throws Exception {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
assertThrows(
@ -213,7 +213,7 @@ class JwtServiceTest {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair));
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
when(authentication.getPrincipal()).thenReturn(userDetails);
when(userDetails.getUsername()).thenReturn(username);
@ -230,7 +230,7 @@ class JwtServiceTest {
@Test
void testExtractClaimsWithInvalidToken() throws Exception {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
assertThrows(
@ -321,7 +321,7 @@ class JwtServiceTest {
when(keystoreService.getActiveKey()).thenReturn(testVerificationKey);
when(keystoreService.getKeyPair("test-key-id")).thenReturn(Optional.of(testKeyPair));
when(keystoreService.decodePublicKey(testVerificationKey.getVerifyingKey()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
when(authentication.getPrincipal()).thenReturn(userDetails);
when(userDetails.getUsername()).thenReturn(username);
@ -347,7 +347,7 @@ 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()))
when(keystoreService.decodePublicKey(testVerificationKey.getKey()))
.thenReturn(testKeyPair.getPublic());
when(authentication.getPrincipal()).thenReturn(userDetails);
when(userDetails.getUsername()).thenReturn(username);
@ -356,8 +356,8 @@ 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(
JwtSigningKey fallbackKey =
new JwtSigningKey(
"fallback-key",
Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded()));

View File

@ -31,7 +31,7 @@ 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 stirling.software.proprietary.security.model.JwtSigningKey;
@ExtendWith(MockitoExtension.class)
class KeyPersistenceServiceInterfaceTest {
@ -87,11 +87,11 @@ class KeyPersistenceServiceInterfaceTest {
keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager);
keyPersistenceService.initializeKeystore();
JwtVerificationKey result = keyPersistenceService.getActiveKey();
JwtSigningKey result = keyPersistenceService.getActiveKey();
assertNotNull(result);
assertNotNull(result.getKeyId());
assertNotNull(result.getVerifyingKey());
assertNotNull(result.getKey());
}
}
@ -103,7 +103,7 @@ class KeyPersistenceServiceInterfaceTest {
String privateKeyBase64 =
Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded());
JwtVerificationKey existingKey = new JwtVerificationKey(keyId, publicKeyBase64);
JwtSigningKey existingKey = new JwtSigningKey(keyId, publicKeyBase64);
Path keyFile = tempDir.resolve(keyId + ".key");
Files.writeString(keyFile, privateKeyBase64);
@ -116,7 +116,7 @@ class KeyPersistenceServiceInterfaceTest {
keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager);
keyPersistenceService.initializeKeystore();
JwtVerificationKey result = keyPersistenceService.getActiveKey();
JwtSigningKey result = keyPersistenceService.getActiveKey();
assertNotNull(result);
assertNotNull(result.getKeyId());
@ -131,7 +131,7 @@ class KeyPersistenceServiceInterfaceTest {
String privateKeyBase64 =
Base64.getEncoder().encodeToString(testKeyPair.getPrivate().getEncoded());
JwtVerificationKey signingKey = new JwtVerificationKey(keyId, publicKeyBase64);
JwtSigningKey signingKey = new JwtSigningKey(keyId, publicKeyBase64);
Path keyFile = tempDir.resolve(keyId + ".key");
Files.writeString(keyFile, privateKeyBase64);
@ -213,7 +213,7 @@ class KeyPersistenceServiceInterfaceTest {
String publicKeyBase64 =
Base64.getEncoder().encodeToString(testKeyPair.getPublic().getEncoded());
JwtVerificationKey existingKey = new JwtVerificationKey(keyId, publicKeyBase64);
JwtSigningKey existingKey = new JwtSigningKey(keyId, publicKeyBase64);
try (MockedStatic<InstallationPathConfig> mockedStatic =
mockStatic(InstallationPathConfig.class)) {
@ -223,10 +223,10 @@ class KeyPersistenceServiceInterfaceTest {
keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager);
keyPersistenceService.initializeKeystore();
JwtVerificationKey result = keyPersistenceService.getActiveKey();
JwtSigningKey result = keyPersistenceService.getActiveKey();
assertNotNull(result);
assertNotNull(result.getKeyId());
assertNotNull(result.getVerifyingKey());
assertNotNull(result.getKey());
}
}
}

View File

@ -26,7 +26,7 @@ ext {
imageioVersion = "3.12.0"
lombokVersion = "1.18.38"
bouncycastleVersion = "1.81"
springSecuritySamlVersion = "6.5.2"
springSecurityVersion = "6.5.2"
openSamlVersion = "4.3.2"
commonmarkVersion = "0.25.1"
googleJavaFormatVersion = "1.28.0"