Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ public static class TempFileManagement {
public String getBaseTmpDir() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better if

        @JsonIgnore
        public String getBaseTmpDir() {
            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() {
            if (libreofficeDir != null && !libreofficeDir.isEmpty()) {
                return libreofficeDir;
            }
            return new File(getBaseTmpDir(), "libreoffice").getPath();
        }

This would eliminate problems with Windows (\) and Linux (/)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String expectedBase =
java.lang.System.getProperty("java.io.tmpdir").replaceAll("/+$", "")
+ "/stirling-pdf";
assertEquals(expectedBase, tfm.getBaseTmpDir());
String expectedLibre = expectedBase + "/libreoffice";
assertEquals(expectedLibre, tfm.getLibreofficeDir());

chnage to

        String expectedBase =
                Paths.get(java.lang.System.getProperty("java.io.tmpdir"), "stirling-pdf").toString();
        assertEquals(expectedBase, tfm.getBaseTmpDir());

        String expectedLibre = Paths.get(expectedBase, "libreoffice").toString();
        assertEquals(expectedLibre, tfm.getLibreofficeDir());

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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package stirling.software.SPDF.controller.api.misc;

import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
Expand All @@ -14,13 +13,17 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import lombok.RequiredArgsConstructor;

import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.WebResponseUtils;

import java.nio.charset.StandardCharsets;
import java.util.Map;

@RestController
@RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs")
Expand Down Expand Up @@ -55,22 +58,25 @@ public ResponseEntity<byte[]> extractHeader(@ModelAttribute PDFFile file) throws

if (jsCodeStr != null && !jsCodeStr.trim().isEmpty()) {
script.append("// File: ")
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
.append(", Script: ")
.append(name)
.append("\n")
.append(jsCodeStr)
.append("\n");
.append(
Filenames.toSimpleFileName(
inputFile.getOriginalFilename()))
.append(", Script: ")
.append(name)
.append("\n")
.append(jsCodeStr)
.append("\n");
foundScript = true;
}
}
}
}

if (!foundScript) {
script = new StringBuilder("PDF '")
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
.append("' does not contain Javascript");
script =
new StringBuilder("PDF '")
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
.append("' does not contain Javascript");
}

return WebResponseUtils.bytesToWebResponse(
Expand Down
6 changes: 3 additions & 3 deletions app/core/src/main/resources/settings.yml.template
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ 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
persistence: false # Set to 'true' to enable JWT key store
enableKeyRotation: false # Set to 'true' to enable key pair rotation
enableKeyCleanup: 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Optional;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
Expand All @@ -26,6 +27,9 @@
@RequiredArgsConstructor
public class InitialSecuritySetup {

@Value("${v2:false}")
private boolean v2Enabled = false;

private final UserService userService;
private final TeamService teamService;
private final ApplicationProperties applicationProperties;
Expand All @@ -43,6 +47,7 @@ public void init() {
}
}

configureJWTSettings();
assignUsersToDefaultTeamIfMissing();
initializeInternalApiUser();
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
Expand All @@ -51,6 +56,19 @@ public void init() {
}
}

private void configureJWTSettings() {
ApplicationProperties.Security.Jwt jwtProperties =
applicationProperties.getSecurity().getJwt();

if (!v2Enabled) {
log.debug("v2 is disabled - disabling all JWT features");
jwtProperties.setEnableKeystore(false);
jwtProperties.setEnableKeyRotation(false);
jwtProperties.setEnableKeyCleanup(false);
jwtProperties.setSecureCookie(false);
}
}

private void assignUsersToDefaultTeamIfMissing() {
Team defaultTeam = teamService.getOrCreateDefaultTeam();
Team internalTeam = teamService.getOrCreateInternalTeam();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public JwtAuthenticationFilter(
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (!jwtService.isJwtEnabled()) {
if (!validateAndNormalizeJwtSettings() && !jwtService.isJwtEnabled()) {
filterChain.doFilter(request, response);
return;
}
Expand Down Expand Up @@ -112,6 +112,36 @@ protected void doFilterInternal(
filterChain.doFilter(request, response);
}

private boolean validateAndNormalizeJwtSettings() {
ApplicationProperties.Security.Jwt jwtProperties = securityProperties.getJwt();

boolean enableKeystore = jwtProperties.isEnableKeystore();
boolean enableKeyRotation = jwtProperties.isEnableKeyRotation();
boolean enableKeyCleanup = jwtProperties.isEnableKeyCleanup();
boolean secureCookie = jwtProperties.isSecureCookie();

// If any JWT property is disabled, disable all JWT properties for consistency
if (!enableKeystore || !enableKeyRotation || !enableKeyCleanup || !secureCookie) {
log.debug(
"One or more JWT properties are disabled - normalizing all JWT settings to false");
log.debug(
"Current settings: keystore={}, rotation={}, cleanup={}, secureCookie={}",
enableKeystore,
enableKeyRotation,
enableKeyCleanup,
secureCookie);

jwtProperties.setEnableKeystore(false);
jwtProperties.setEnableKeyRotation(false);
jwtProperties.setEnableKeyCleanup(false);
jwtProperties.setSecureCookie(false);

return false;
}

return true;
}

private boolean apiKeyExists(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx
*/
private AuthenticationType determinePreferredSSOType() {
// Check what SSO types are enabled and prefer in order: OAUTH2 > SAML2 > fallback to OAUTH2
boolean oauth2Enabled = securityProperties.getOauth2() != null && securityProperties.getOauth2().getEnabled();
boolean saml2Enabled = securityProperties.getSaml2() != null && securityProperties.getSaml2().getEnabled();
boolean oauth2Enabled =
securityProperties.getOauth2() != null
&& securityProperties.getOauth2().getEnabled();
boolean saml2Enabled =
securityProperties.getSaml2() != null && securityProperties.getSaml2().getEnabled();

if (oauth2Enabled) {
return AuthenticationType.OAUTH2;
Expand Down
Loading