mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-01 20:10:35 +01:00
Defaulting JWT settings to false (#4416)
Defaulting the configuration settings for Stirling PDF's JWT to false to avoid any unexpected issues
This commit is contained in:
parent
7bd31a954e
commit
dabc52ef73
@ -303,11 +303,10 @@ public class ApplicationProperties {
|
||||
|
||||
@Data
|
||||
public static class Jwt {
|
||||
private boolean enableKeystore = true;
|
||||
private boolean enableKeyRotation = false;
|
||||
private boolean enableKeyCleanup = true;
|
||||
private boolean enabled = true;
|
||||
private boolean keyCleanup = true;
|
||||
private int keyRetentionDays = 7;
|
||||
private boolean secureCookie;
|
||||
private Boolean secureCookie;
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,16 +376,19 @@ public class ApplicationProperties {
|
||||
|
||||
@JsonIgnore
|
||||
public String getBaseTmpDir() {
|
||||
return baseTmpDir != null && !baseTmpDir.isEmpty()
|
||||
? baseTmpDir
|
||||
: java.lang.System.getProperty("java.io.tmpdir") + "/stirling-pdf";
|
||||
if (baseTmpDir != null && !baseTmpDir.isEmpty()) {
|
||||
return baseTmpDir;
|
||||
}
|
||||
String tmp = java.lang.System.getProperty("java.io.tmpdir");
|
||||
return new File(tmp, "stirling-pdf").getPath();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getLibreofficeDir() {
|
||||
return libreofficeDir != null && !libreofficeDir.isEmpty()
|
||||
? libreofficeDir
|
||||
: getBaseTmpDir() + "/libreoffice";
|
||||
if (libreofficeDir != null && !libreofficeDir.isEmpty()) {
|
||||
return libreofficeDir;
|
||||
}
|
||||
return new File(getBaseTmpDir(), "libreoffice").getPath();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -636,7 +636,7 @@ public class PdfUtils {
|
||||
case "equal" -> actualPageCount == pageCount;
|
||||
case "less" -> actualPageCount < pageCount;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
|
||||
throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
|
||||
};
|
||||
}
|
||||
|
||||
@ -659,15 +659,9 @@ public class PdfUtils {
|
||||
return actualPageWidth == expectedPageWidth && actualPageHeight == expectedPageHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key for storing the dimensions of a rendered image in a map.
|
||||
*/
|
||||
private record PdfRenderSettingsKey(float mediaBoxWidth, float mediaBoxHeight, int rotation) {
|
||||
}
|
||||
/** Key for storing the dimensions of a rendered image in a map. */
|
||||
private record PdfRenderSettingsKey(float mediaBoxWidth, float mediaBoxHeight, int rotation) {}
|
||||
|
||||
/**
|
||||
* Value for storing the dimensions of a rendered image in a map.
|
||||
*/
|
||||
private record PdfImageDimensionValue(int width, int height) {
|
||||
}
|
||||
/** Value for storing the dimensions of a rendered image in a map. */
|
||||
private record PdfImageDimensionValue(int width, int height) {}
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@ import stirling.software.common.model.ApplicationProperties.Driver;
|
||||
import stirling.software.common.model.ApplicationProperties.Premium;
|
||||
import stirling.software.common.model.ApplicationProperties.Security;
|
||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
|
||||
class ApplicationPropertiesLogicTest {
|
||||
|
||||
@ -39,15 +38,12 @@ class ApplicationPropertiesLogicTest {
|
||||
new ApplicationProperties.TempFileManagement();
|
||||
|
||||
String expectedBase =
|
||||
RegexPatternUtils.getInstance()
|
||||
.getTrailingSlashesPattern()
|
||||
.matcher(java.lang.System.getProperty("java.io.tmpdir"))
|
||||
.replaceAll("")
|
||||
+ "/stirling-pdf";
|
||||
assertEquals(expectedBase, normalize.apply(tfm.getBaseTmpDir()));
|
||||
Paths.get(java.lang.System.getProperty("java.io.tmpdir"), "stirling-pdf")
|
||||
.toString();
|
||||
assertEquals(expectedBase, tfm.getBaseTmpDir());
|
||||
|
||||
String expectedLibre = expectedBase + "/libreoffice";
|
||||
assertEquals(expectedLibre, normalize.apply(tfm.getLibreofficeDir()));
|
||||
String expectedLibre = Paths.get(expectedBase, "libreoffice").toString();
|
||||
assertEquals(expectedLibre, tfm.getLibreofficeDir());
|
||||
|
||||
tfm.setBaseTmpDir("/custom/base");
|
||||
assertEquals("/custom/base", normalize.apply(tfm.getBaseTmpDir()));
|
||||
|
||||
@ -227,7 +227,9 @@ public class LoadingWindow extends JDialog {
|
||||
if (!existingPids
|
||||
.contains(
|
||||
pid)) {
|
||||
log.debug("Found new explorer.exe with PID: {}", pid);
|
||||
log.debug(
|
||||
"Found new explorer.exe with PID: {}",
|
||||
pid);
|
||||
ProcessBuilder
|
||||
killProcess =
|
||||
new ProcessBuilder(
|
||||
@ -245,7 +247,9 @@ public class LoadingWindow extends JDialog {
|
||||
2,
|
||||
TimeUnit
|
||||
.SECONDS);
|
||||
log.debug("Explorer process terminated: {}", pid);
|
||||
log.debug(
|
||||
"Explorer process terminated: {}",
|
||||
pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,13 +87,15 @@ public class FilterController {
|
||||
PDDocument document = pdfDocumentFactory.load(inputFile);
|
||||
int actualPageCount = document.getNumberOfPages();
|
||||
// Perform the comparison
|
||||
boolean valid = switch (comparator) {
|
||||
case "Greater" -> actualPageCount > pageCount;
|
||||
case "Equal" -> actualPageCount == pageCount;
|
||||
case "Less" -> actualPageCount < pageCount;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
|
||||
};
|
||||
boolean valid =
|
||||
switch (comparator) {
|
||||
case "Greater" -> actualPageCount > pageCount;
|
||||
case "Equal" -> actualPageCount == pageCount;
|
||||
case "Less" -> actualPageCount < pageCount;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException(
|
||||
"comparator", comparator);
|
||||
};
|
||||
|
||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||
return null;
|
||||
@ -123,13 +125,15 @@ public class FilterController {
|
||||
float standardArea = standardSize.getWidth() * standardSize.getHeight();
|
||||
|
||||
// Perform the comparison
|
||||
boolean valid = switch (comparator) {
|
||||
case "Greater" -> actualArea > standardArea;
|
||||
case "Equal" -> actualArea == standardArea;
|
||||
case "Less" -> actualArea < standardArea;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
|
||||
};
|
||||
boolean valid =
|
||||
switch (comparator) {
|
||||
case "Greater" -> actualArea > standardArea;
|
||||
case "Equal" -> actualArea == standardArea;
|
||||
case "Less" -> actualArea < standardArea;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException(
|
||||
"comparator", comparator);
|
||||
};
|
||||
|
||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||
return null;
|
||||
@ -149,13 +153,15 @@ public class FilterController {
|
||||
long actualFileSize = inputFile.getSize();
|
||||
|
||||
// Perform the comparison
|
||||
boolean valid = switch (comparator) {
|
||||
case "Greater" -> actualFileSize > fileSize;
|
||||
case "Equal" -> actualFileSize == fileSize;
|
||||
case "Less" -> actualFileSize < fileSize;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
|
||||
};
|
||||
boolean valid =
|
||||
switch (comparator) {
|
||||
case "Greater" -> actualFileSize > fileSize;
|
||||
case "Equal" -> actualFileSize == fileSize;
|
||||
case "Less" -> actualFileSize < fileSize;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException(
|
||||
"comparator", comparator);
|
||||
};
|
||||
|
||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||
return null;
|
||||
@ -179,13 +185,15 @@ public class FilterController {
|
||||
int actualRotation = firstPage.getRotation();
|
||||
|
||||
// Perform the comparison
|
||||
boolean valid = switch (comparator) {
|
||||
case "Greater" -> actualRotation > rotation;
|
||||
case "Equal" -> actualRotation == rotation;
|
||||
case "Less" -> actualRotation < rotation;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
|
||||
};
|
||||
boolean valid =
|
||||
switch (comparator) {
|
||||
case "Greater" -> actualRotation > rotation;
|
||||
case "Equal" -> actualRotation == rotation;
|
||||
case "Less" -> actualRotation < rotation;
|
||||
default ->
|
||||
throw ExceptionUtils.createInvalidArgumentException(
|
||||
"comparator", comparator);
|
||||
};
|
||||
|
||||
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
|
||||
return null;
|
||||
|
||||
@ -23,9 +23,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.MetadataRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.service.PdfMetadataService;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.service.PdfMetadataService;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
import stirling.software.common.util.propertyeditor.StringToMapPropertyEditor;
|
||||
|
||||
|
||||
@ -170,14 +170,15 @@ public class WatermarkController {
|
||||
throws IOException {
|
||||
String resourceDir = "";
|
||||
PDFont font = new PDType1Font(Standard14Fonts.FontName.HELVETICA);
|
||||
resourceDir = switch (alphabet) {
|
||||
case "arabic" -> "static/fonts/NotoSansArabic-Regular.ttf";
|
||||
case "japanese" -> "static/fonts/Meiryo.ttf";
|
||||
case "korean" -> "static/fonts/malgun.ttf";
|
||||
case "chinese" -> "static/fonts/SimSun.ttf";
|
||||
case "thai" -> "static/fonts/NotoSansThai-Regular.ttf";
|
||||
default -> "static/fonts/NotoSans-Regular.ttf";
|
||||
};
|
||||
resourceDir =
|
||||
switch (alphabet) {
|
||||
case "arabic" -> "static/fonts/NotoSansArabic-Regular.ttf";
|
||||
case "japanese" -> "static/fonts/Meiryo.ttf";
|
||||
case "korean" -> "static/fonts/malgun.ttf";
|
||||
case "chinese" -> "static/fonts/SimSun.ttf";
|
||||
case "thai" -> "static/fonts/NotoSansThai-Regular.ttf";
|
||||
default -> "static/fonts/NotoSans-Regular.ttf";
|
||||
};
|
||||
|
||||
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
|
||||
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
|
||||
|
||||
@ -288,8 +288,8 @@ public class GeneralWebController {
|
||||
case "eot" -> "embedded-opentype";
|
||||
case "svg" -> "svg";
|
||||
default ->
|
||||
// or throw an exception if an unexpected extension is encountered
|
||||
"";
|
||||
// or throw an exception if an unexpected extension is encountered
|
||||
"";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -60,11 +60,10 @@ security:
|
||||
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
||||
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
||||
jwt: # This feature is currently under development and not yet fully supported. Do not use in production.
|
||||
persistence: true # Set to 'true' to enable JWT key store
|
||||
enableKeyRotation: true # Set to 'true' to enable key pair rotation
|
||||
enableKeyCleanup: true # Set to 'true' to enable key pair cleanup
|
||||
enabled: false # Set to 'true' to enable JWT key store
|
||||
keyCleanup: false # Set to 'true' to enable key pair cleanup
|
||||
keyRetentionDays: 7 # Number of days to retain old keys. The default is 7 days.
|
||||
secureCookie: false # Set to 'true' to use secure cookies for JWTs
|
||||
secureCookie: true # Set to 'true' to use secure cookies for JWTs
|
||||
|
||||
premium:
|
||||
key: 00000000-0000-0000-0000-000000000000
|
||||
|
||||
@ -5,6 +5,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@ -26,6 +27,9 @@ import stirling.software.proprietary.security.service.UserService;
|
||||
@RequiredArgsConstructor
|
||||
public class InitialSecuritySetup {
|
||||
|
||||
@Value("${v2:false}")
|
||||
private boolean v2Enabled = false;
|
||||
|
||||
private final UserService userService;
|
||||
private final TeamService teamService;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
@ -43,6 +47,7 @@ public class InitialSecuritySetup {
|
||||
}
|
||||
}
|
||||
|
||||
configureJWTSettings();
|
||||
assignUsersToDefaultTeamIfMissing();
|
||||
initializeInternalApiUser();
|
||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||
@ -51,6 +56,18 @@ public class InitialSecuritySetup {
|
||||
}
|
||||
}
|
||||
|
||||
private void configureJWTSettings() {
|
||||
ApplicationProperties.Security.Jwt jwtProperties =
|
||||
applicationProperties.getSecurity().getJwt();
|
||||
|
||||
boolean jwtEnabled = jwtProperties.isEnabled();
|
||||
if (!v2Enabled || !jwtEnabled) {
|
||||
log.debug("V2 enabled: {}, JWT enabled: {} - disabling all JWT features", v2Enabled, jwtEnabled);
|
||||
|
||||
jwtProperties.setKeyCleanup(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void assignUsersToDefaultTeamIfMissing() {
|
||||
Team defaultTeam = teamService.getOrCreateDefaultTeam();
|
||||
Team internalTeam = teamService.getOrCreateInternalTeam();
|
||||
|
||||
@ -40,7 +40,7 @@ public class KeyPairCleanupService {
|
||||
@PostConstruct
|
||||
@Scheduled(fixedDelay = 1, timeUnit = TimeUnit.DAYS)
|
||||
public void cleanup() {
|
||||
if (!jwtProperties.isEnableKeyCleanup() || !keyPersistenceService.isKeystoreEnabled()) {
|
||||
if (!jwtProperties.isEnabled() || !jwtProperties.isKeyCleanup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ public class KeyPairCleanupService {
|
||||
}
|
||||
|
||||
private void removePrivateKey(String keyId) throws IOException {
|
||||
if (!keyPersistenceService.isKeystoreEnabled()) {
|
||||
if (!jwtProperties.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
|
||||
@PostConstruct
|
||||
public void initializeKeystore() {
|
||||
if (!isKeystoreEnabled()) {
|
||||
if (!jwtProperties.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
|
||||
@Override
|
||||
public Optional<KeyPair> getKeyPair(String keyId) {
|
||||
if (!isKeystoreEnabled()) {
|
||||
if (!jwtProperties.isEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@ -155,11 +155,6 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeystoreEnabled() {
|
||||
return jwtProperties.isEnableKeystore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtVerificationKey refreshActiveKeyPair() {
|
||||
return generateAndStoreKeypair();
|
||||
@ -268,4 +263,8 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
return keyFactory.generatePublic(keySpec);
|
||||
}
|
||||
|
||||
public boolean isKeystoreEnabled() {
|
||||
return jwtProperties.isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,8 +16,6 @@ public interface KeyPersistenceServiceInterface {
|
||||
|
||||
Optional<KeyPair> getKeyPair(String keyId);
|
||||
|
||||
boolean isKeystoreEnabled();
|
||||
|
||||
JwtVerificationKey refreshActiveKeyPair();
|
||||
|
||||
List<JwtVerificationKey> getKeysEligibleForCleanup(LocalDateTime cutoffDate);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -21,8 +20,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@ -58,23 +55,7 @@ class KeyPersistenceServiceInterfaceTest {
|
||||
|
||||
lenient().when(applicationProperties.getSecurity()).thenReturn(security);
|
||||
lenient().when(security.getJwt()).thenReturn(jwtConfig);
|
||||
lenient().when(jwtConfig.isEnableKeystore()).thenReturn(true); // Default value
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testKeystoreEnabled(boolean keystoreEnabled) {
|
||||
when(jwtConfig.isEnableKeystore()).thenReturn(keystoreEnabled);
|
||||
|
||||
try (MockedStatic<InstallationPathConfig> mockedStatic =
|
||||
mockStatic(InstallationPathConfig.class)) {
|
||||
mockedStatic
|
||||
.when(InstallationPathConfig::getPrivateKeyPath)
|
||||
.thenReturn(tempDir.toString());
|
||||
keyPersistenceService = new KeyPersistenceService(applicationProperties, cacheManager);
|
||||
|
||||
assertEquals(keystoreEnabled, keyPersistenceService.isKeystoreEnabled());
|
||||
}
|
||||
lenient().when(jwtConfig.isEnabled()).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -177,7 +158,7 @@ class KeyPersistenceServiceInterfaceTest {
|
||||
|
||||
@Test
|
||||
void testGetKeyPairWhenKeystoreDisabled() {
|
||||
when(jwtConfig.isEnableKeystore()).thenReturn(false);
|
||||
when(jwtConfig.isEnabled()).thenReturn(false);
|
||||
|
||||
try (MockedStatic<InstallationPathConfig> mockedStatic =
|
||||
mockStatic(InstallationPathConfig.class)) {
|
||||
|
||||
@ -59,6 +59,11 @@ security:
|
||||
idpCert: classpath:okta.cert # The certificate your Provider will use to authenticate your app's SAML authentication requests. Provided by your Provider
|
||||
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
||||
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
||||
jwt: # This feature is currently under development and not yet fully supported. Do not use in production.
|
||||
enabled: true # Set to 'true' to enable JWT key store
|
||||
keyCleanup: true # Set to 'true' to enable key pair cleanup
|
||||
keyRetentionDays: 7 # Number of days to retain old keys. The default is 7 days.
|
||||
secureCookie: false # Set to 'true' to use secure cookies for JWTs
|
||||
|
||||
premium:
|
||||
key: 00000000-0000-0000-0000-000000000000
|
||||
|
||||
Loading…
Reference in New Issue
Block a user