mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-02 18:45:21 +00:00
wip
This commit is contained in:
parent
d197285a56
commit
71513ba762
@ -60,11 +60,12 @@ security:
|
|||||||
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
||||||
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
||||||
jwt:
|
jwt:
|
||||||
enableKeyStore: true # Set to 'true' to enable JWT key store
|
persistence: true # Set to 'true' to enable JWT key store
|
||||||
enableKeyRotation: true # Set to 'true' to enable JWT key rotation
|
enableKeyRotation: true # Set to 'true' to enable key pair rotation
|
||||||
enableKeyCleanup: true # Set to 'true' to enable JWT key cleanup
|
enableKeyCleanup: true # Set to 'true' to enable key pair cleanup
|
||||||
keyRetentionDays: 7 # Number of days to retain old keys. The default is 7 days.
|
keyRetentionDays: 7 # Number of days to retain old keys. The default is 7 days.
|
||||||
cleanupBatchSize: 100 # Number of keys to clean up in each batch. The default is 100.
|
cleanupBatchSize: 100 # Number of keys to clean up in each batch. The default is 100.
|
||||||
|
secureCookie: false # Set to 'true' to use secure cookies for JWTs
|
||||||
|
|
||||||
premium:
|
premium:
|
||||||
key: 00000000-0000-0000-0000-000000000000
|
key: 00000000-0000-0000-0000-000000000000
|
||||||
|
@ -10,6 +10,7 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseCookie;
|
import org.springframework.http.ResponseCookie;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@ -43,6 +44,9 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
private static final String ISSUER = "Stirling PDF";
|
private static final String ISSUER = "Stirling PDF";
|
||||||
private static final long EXPIRATION = 3600000;
|
private static final long EXPIRATION = 3600000;
|
||||||
|
|
||||||
|
@Value("${stirling.security.jwt.secureCookie:true}")
|
||||||
|
private boolean secureCookie;
|
||||||
|
|
||||||
private final KeystoreServiceInterface keystoreService;
|
private final KeystoreServiceInterface keystoreService;
|
||||||
private final boolean v2Enabled;
|
private final boolean v2Enabled;
|
||||||
|
|
||||||
@ -198,7 +202,7 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
ResponseCookie cookie =
|
ResponseCookie cookie =
|
||||||
ResponseCookie.from(JWT_COOKIE_NAME, Newlines.stripAll(token))
|
ResponseCookie.from(JWT_COOKIE_NAME, Newlines.stripAll(token))
|
||||||
.httpOnly(true)
|
.httpOnly(true)
|
||||||
// .secure(true) // todo: fix, make configurable
|
.secure(secureCookie)
|
||||||
.sameSite("Strict")
|
.sameSite("Strict")
|
||||||
.maxAge(EXPIRATION / 1000)
|
.maxAge(EXPIRATION / 1000)
|
||||||
.path("/")
|
.path("/")
|
||||||
@ -214,7 +218,7 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
ResponseCookie cookie =
|
ResponseCookie cookie =
|
||||||
ResponseCookie.from(JWT_COOKIE_NAME, "")
|
ResponseCookie.from(JWT_COOKIE_NAME, "")
|
||||||
.httpOnly(true)
|
.httpOnly(true)
|
||||||
.secure(true)
|
.secure(secureCookie)
|
||||||
.sameSite("None")
|
.sameSite("None")
|
||||||
.maxAge(0)
|
.maxAge(0)
|
||||||
.path("/")
|
.path("/")
|
||||||
|
@ -11,6 +11,8 @@ import java.util.Optional;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
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.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -201,19 +203,10 @@ class JwtServiceTest {
|
|||||||
assertThrows(AuthenticationFailureException.class, () -> jwtService.extractClaims("invalid-token"));
|
assertThrows(AuthenticationFailureException.class, () -> jwtService.extractClaims("invalid-token"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testExtractTokenWithAuthorizationHeader() {
|
|
||||||
String token = "test-token";
|
|
||||||
when(request.getHeader("Authorization")).thenReturn("Bearer " + token);
|
|
||||||
|
|
||||||
assertEquals(token, jwtService.extractToken(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithCookie() {
|
void testExtractTokenWithCookie() {
|
||||||
String token = "test-token";
|
String token = "test-token";
|
||||||
Cookie[] cookies = { new Cookie("stirling_jwt", token) };
|
Cookie[] cookies = { new Cookie("stirling_jwt", token) };
|
||||||
when(request.getHeader("Authorization")).thenReturn(null);
|
|
||||||
when(request.getCookies()).thenReturn(cookies);
|
when(request.getCookies()).thenReturn(cookies);
|
||||||
|
|
||||||
assertEquals(token, jwtService.extractToken(request));
|
assertEquals(token, jwtService.extractToken(request));
|
||||||
@ -221,7 +214,6 @@ class JwtServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithNoCookies() {
|
void testExtractTokenWithNoCookies() {
|
||||||
when(request.getHeader("Authorization")).thenReturn(null);
|
|
||||||
when(request.getCookies()).thenReturn(null);
|
when(request.getCookies()).thenReturn(null);
|
||||||
|
|
||||||
assertNull(jwtService.extractToken(request));
|
assertNull(jwtService.extractToken(request));
|
||||||
@ -230,7 +222,6 @@ class JwtServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithWrongCookie() {
|
void testExtractTokenWithWrongCookie() {
|
||||||
Cookie[] cookies = {new Cookie("OTHER_COOKIE", "value")};
|
Cookie[] cookies = {new Cookie("OTHER_COOKIE", "value")};
|
||||||
when(request.getHeader("Authorization")).thenReturn(null);
|
|
||||||
when(request.getCookies()).thenReturn(cookies);
|
when(request.getCookies()).thenReturn(cookies);
|
||||||
|
|
||||||
assertNull(jwtService.extractToken(request));
|
assertNull(jwtService.extractToken(request));
|
||||||
@ -238,22 +229,30 @@ class JwtServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractTokenWithInvalidAuthorizationHeader() {
|
void testExtractTokenWithInvalidAuthorizationHeader() {
|
||||||
when(request.getHeader("Authorization")).thenReturn("Basic token");
|
|
||||||
when(request.getCookies()).thenReturn(null);
|
when(request.getCookies()).thenReturn(null);
|
||||||
|
|
||||||
assertNull(jwtService.extractToken(request));
|
assertNull(jwtService.extractToken(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
void testAddToken() {
|
@ValueSource(booleans = {true, false})
|
||||||
|
void testAddToken(boolean secureCookie) throws Exception {
|
||||||
String token = "test-token";
|
String token = "test-token";
|
||||||
|
|
||||||
jwtService.addToken(response, token);
|
// Create new JwtService instance with the secureCookie parameter
|
||||||
|
JwtService testJwtService = createJwtServiceWithSecureCookie(secureCookie);
|
||||||
|
|
||||||
|
testJwtService.addToken(response, token);
|
||||||
|
|
||||||
verify(response).setHeader("Authorization", "Bearer " + token);
|
verify(response).setHeader("Authorization", "Bearer " + token);
|
||||||
verify(response).addHeader(eq("Set-Cookie"), contains("stirling_jwt=" + token));
|
verify(response).addHeader(eq("Set-Cookie"), contains("stirling_jwt=" + token));
|
||||||
verify(response).addHeader(eq("Set-Cookie"), contains("HttpOnly"));
|
verify(response).addHeader(eq("Set-Cookie"), contains("HttpOnly"));
|
||||||
verify(response).addHeader(eq("Set-Cookie"), contains("Secure"));
|
|
||||||
|
if (secureCookie) {
|
||||||
|
verify(response).addHeader(eq("Set-Cookie"), contains("Secure"));
|
||||||
|
} else {
|
||||||
|
verify(response, org.mockito.Mockito.never()).addHeader(eq("Set-Cookie"), contains("Secure"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -327,4 +326,16 @@ class JwtServiceTest {
|
|||||||
// Verify fallback to active keypair was used (called multiple times during token operations)
|
// Verify fallback to active keypair was used (called multiple times during token operations)
|
||||||
verify(keystoreService, atLeast(1)).getActiveKeyPair();
|
verify(keystoreService, atLeast(1)).getActiveKeyPair();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JwtService createJwtServiceWithSecureCookie(boolean secureCookie) throws Exception {
|
||||||
|
// Use reflection to create JwtService with custom secureCookie value
|
||||||
|
JwtService testService = new JwtService(true, keystoreService);
|
||||||
|
|
||||||
|
// Set the secureCookie field using reflection
|
||||||
|
java.lang.reflect.Field secureCookieField = JwtService.class.getDeclaredField("secureCookie");
|
||||||
|
secureCookieField.setAccessible(true);
|
||||||
|
secureCookieField.set(testService, secureCookie);
|
||||||
|
|
||||||
|
return testService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user