refactor: replace legacy Paths usage with Path.of (#6441)

This commit is contained in:
Ludy
2026-06-14 15:48:28 +01:00
committed by GitHub
parent dad2425c27
commit 1ce765ab1e
50 changed files with 178 additions and 279 deletions
@@ -3,7 +3,6 @@ package stirling.software.common.configuration;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Properties; import java.util.Properties;
@@ -122,17 +121,17 @@ public class AppConfig {
@Bean(name = "RunningInDocker") @Bean(name = "RunningInDocker")
public boolean runningInDocker() { public boolean runningInDocker() {
return Files.exists(Paths.get("/.dockerenv")); return Files.exists(Path.of("/.dockerenv"));
} }
@Bean(name = "configDirMounted") @Bean(name = "configDirMounted")
public boolean isRunningInDockerWithConfig() { public boolean isRunningInDockerWithConfig() {
Path dockerEnv = Paths.get("/.dockerenv"); Path dockerEnv = Path.of("/.dockerenv");
// default to true if not docker // default to true if not docker
if (!Files.exists(dockerEnv)) { if (!Files.exists(dockerEnv)) {
return true; return true;
} }
Path mountInfo = Paths.get("/proc/1/mountinfo"); Path mountInfo = Path.of("/proc/1/mountinfo");
// this should always exist, if not some unknown usecase // this should always exist, if not some unknown usecase
if (!Files.exists(mountInfo)) { if (!Files.exists(mountInfo)) {
return true; return true;
@@ -7,7 +7,6 @@ import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.List; import java.util.List;
@@ -27,7 +26,7 @@ public class ConfigInitializer {
public void ensureConfigExists() throws IOException, URISyntaxException { public void ensureConfigExists() throws IOException, URISyntaxException {
// 1) If settings file doesn't exist, create from template // 1) If settings file doesn't exist, create from template
Path destPath = Paths.get(InstallationPathConfig.getSettingsPath()); Path destPath = Path.of(InstallationPathConfig.getSettingsPath());
boolean settingsFileExists = Files.exists(destPath); boolean settingsFileExists = Files.exists(destPath);
@@ -39,7 +38,7 @@ public class ConfigInitializer {
if (settingsFileExists) { if (settingsFileExists) {
// move settings.yml to settings.yml.{timestamp}.bak // move settings.yml to settings.yml.{timestamp}.bak
Path backupPath = Path backupPath =
Paths.get( Path.of(
InstallationPathConfig.getSettingsPath() InstallationPathConfig.getSettingsPath()
+ "." + "."
+ System.currentTimeMillis() + System.currentTimeMillis()
@@ -96,7 +95,7 @@ public class ConfigInitializer {
} }
// 3) Ensure custom settings file exists // 3) Ensure custom settings file exists
Path customSettingsPath = Paths.get(InstallationPathConfig.getCustomSettingsPath()); Path customSettingsPath = Path.of(InstallationPathConfig.getCustomSettingsPath());
if (Files.notExists(customSettingsPath)) { if (Files.notExists(customSettingsPath)) {
Files.createFile(customSettingsPath); Files.createFile(customSettingsPath);
log.info("Created custom_settings file: {}", customSettingsPath); log.info("Created custom_settings file: {}", customSettingsPath);
@@ -3,7 +3,6 @@ package stirling.software.common.configuration;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@@ -201,7 +200,7 @@ public class RuntimePathConfig {
try { try {
// Normalize to absolute path // Normalize to absolute path
Path path = Paths.get(pathStr.trim()).toAbsolutePath().normalize(); Path path = Path.of(pathStr.trim()).toAbsolutePath().normalize();
String normalizedPath = path.toString(); String normalizedPath = path.toString();
// Check for duplicates // Check for duplicates
@@ -224,9 +223,9 @@ public class RuntimePathConfig {
private void detectOverlappingPaths(List<String> paths) { private void detectOverlappingPaths(List<String> paths) {
for (int i = 0; i < paths.size(); i++) { for (int i = 0; i < paths.size(); i++) {
Path path1 = Paths.get(paths.get(i)); Path path1 = Path.of(paths.get(i));
for (int j = i + 1; j < paths.size(); j++) { for (int j = i + 1; j < paths.size(); j++) {
Path path2 = Paths.get(paths.get(j)); Path path2 = Path.of(paths.get(j));
// Check if one path is a parent of the other // Check if one path is a parent of the other
if (path1.startsWith(path2)) { if (path1.startsWith(path2)) {
@@ -246,10 +245,10 @@ public class RuntimePathConfig {
private void validatePipelinePaths() { private void validatePipelinePaths() {
try { try {
Path finishedPath = Paths.get(pipelineFinishedFoldersPath).toAbsolutePath().normalize(); Path finishedPath = Path.of(pipelineFinishedFoldersPath).toAbsolutePath().normalize();
for (String watchedPathStr : pipelineWatchedFoldersPaths) { for (String watchedPathStr : pipelineWatchedFoldersPaths) {
Path watchedPath = Paths.get(watchedPathStr).toAbsolutePath().normalize(); Path watchedPath = Path.of(watchedPathStr).toAbsolutePath().normalize();
// Check if watched folder is same as finished folder // Check if watched folder is same as finished folder
if (watchedPath.equals(finishedPath)) { if (watchedPath.equals(finishedPath)) {
@@ -1,7 +1,6 @@
package stirling.software.common.model; package stirling.software.common.model;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Locale; import java.util.Locale;
@@ -24,7 +23,7 @@ public class FileInfo {
// Converts the file path string to a Path object. // Converts the file path string to a Path object.
public Path getFilePathAsPath() { public Path getFilePathAsPath() {
return Paths.get(filePath); return Path.of(filePath);
} }
// Formats the file size into a human-readable string. // Formats the file size into a human-readable string.
@@ -3,7 +3,6 @@ package stirling.software.common.service;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -35,7 +34,7 @@ public class MobileScannerService {
public MobileScannerService() throws IOException { public MobileScannerService() throws IOException {
// Create temp directory for mobile scanner uploads // Create temp directory for mobile scanner uploads
this.tempDirectory = this.tempDirectory =
Paths.get(System.getProperty("java.io.tmpdir"), "stirling-mobile-scanner"); Path.of(System.getProperty("java.io.tmpdir"), "stirling-mobile-scanner");
Files.createDirectories(tempDirectory); Files.createDirectories(tempDirectory);
log.info("Mobile scanner temp directory: {}", tempDirectory); log.info("Mobile scanner temp directory: {}", tempDirectory);
} }
@@ -8,7 +8,7 @@ import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean; import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean; import java.lang.management.ThreadMXBean;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@@ -160,7 +160,7 @@ public class PostHogService {
} }
private boolean isRunningInDocker() { private boolean isRunningInDocker() {
return Files.exists(Paths.get("/.dockerenv")); return Files.exists(Path.of("/.dockerenv"));
} }
private Map<String, Object> getDockerMetrics() { private Map<String, Object> getDockerMetrics() {
@@ -255,7 +255,7 @@ public class GeneralUtils {
String pattern = locationPattern; String pattern = locationPattern;
if (pattern.startsWith("file:")) { if (pattern.startsWith("file:")) {
String rawPath = pattern.substring(5).replace("\\*", "").replace("/*", ""); String rawPath = pattern.substring(5).replace("\\*", "").replace("/*", "");
Path normalizePath = Paths.get(rawPath).normalize(); Path normalizePath = Path.of(rawPath).normalize();
pattern = "file:" + normalizePath.toString().replace("\\", "/") + "/*"; pattern = "file:" + normalizePath.toString().replace("\\", "/") + "/*";
} }
return ResourcePatternUtils.getResourcePatternResolver(resourceLoader) return ResourcePatternUtils.getResourcePatternResolver(resourceLoader)
@@ -837,7 +837,7 @@ public class GeneralUtils {
} }
public boolean createDir(String path) { public boolean createDir(String path) {
Path folder = Paths.get(path); Path folder = Path.of(path);
if (!Files.exists(folder)) { if (!Files.exists(folder)) {
try { try {
Files.createDirectories(folder); Files.createDirectories(folder);
@@ -867,7 +867,7 @@ public class GeneralUtils {
public void saveKeyToSettings(String key, Object newValue) throws IOException { public void saveKeyToSettings(String key, Object newValue) throws IOException {
String[] keyArray = key.split("\\."); String[] keyArray = key.split("\\.");
Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath()); Path settingsPath = Path.of(InstallationPathConfig.getSettingsPath());
YamlHelper settingsYaml = new YamlHelper(settingsPath); YamlHelper settingsYaml = new YamlHelper(settingsPath);
settingsYaml.updateValue(Arrays.asList(keyArray), newValue); settingsYaml.updateValue(Arrays.asList(keyArray), newValue);
settingsYaml.saveOverride(settingsPath); settingsYaml.saveOverride(settingsPath);
@@ -888,7 +888,7 @@ public class GeneralUtils {
return; return;
} }
Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath()); Path settingsPath = Path.of(InstallationPathConfig.getSettingsPath());
YamlHelper settingsYaml = new YamlHelper(settingsPath); YamlHelper settingsYaml = new YamlHelper(settingsPath);
// Apply all updates to the same YamlHelper instance // Apply all updates to the same YamlHelper instance
@@ -974,11 +974,11 @@ public class GeneralUtils {
*/ */
public void extractPipeline() throws IOException { public void extractPipeline() throws IOException {
Path pipelineDir = Path pipelineDir =
Paths.get(InstallationPathConfig.getPipelinePath(), DEFAULT_WEBUI_CONFIGS_DIR); Path.of(InstallationPathConfig.getPipelinePath(), DEFAULT_WEBUI_CONFIGS_DIR);
Files.createDirectories(pipelineDir); Files.createDirectories(pipelineDir);
for (String name : DEFAULT_VALID_PIPELINE) { for (String name : DEFAULT_VALID_PIPELINE) {
if (!Paths.get(name).getFileName().toString().equals(name)) { if (!Path.of(name).getFileName().toString().equals(name)) {
log.error("Invalid pipeline file name: {}", name); log.error("Invalid pipeline file name: {}", name);
throw new IllegalArgumentException("Invalid pipeline file name: " + name); throw new IllegalArgumentException("Invalid pipeline file name: " + name);
} }
@@ -1014,7 +1014,7 @@ public class GeneralUtils {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"scriptName must not contain path traversal characters"); "scriptName must not contain path traversal characters");
} }
if (!Paths.get(scriptName).getFileName().toString().equals(scriptName)) { if (!Path.of(scriptName).getFileName().toString().equals(scriptName)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"scriptName must not contain path traversal characters"); "scriptName must not contain path traversal characters");
} }
@@ -1024,7 +1024,7 @@ public class GeneralUtils {
"scriptName must be either 'png_to_webp.py' or 'split_photos.py'"); "scriptName must be either 'png_to_webp.py' or 'split_photos.py'");
} }
Path scriptsDir = Paths.get(InstallationPathConfig.getScriptsPath(), PYTHON_SCRIPTS_DIR); Path scriptsDir = Path.of(InstallationPathConfig.getScriptsPath(), PYTHON_SCRIPTS_DIR);
Files.createDirectories(scriptsDir); Files.createDirectories(scriptsDir);
Path target = scriptsDir.resolve(scriptName); Path target = scriptsDir.resolve(scriptName);
@@ -4,7 +4,6 @@ import java.io.File;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -20,7 +19,7 @@ public class JarPathUtil {
public static Path currentJar() { public static Path currentJar() {
try { try {
Path jar = Path jar =
Paths.get( Path.of(
JarPathUtil.class JarPathUtil.class
.getProtectionDomain() .getProtectionDomain()
.getCodeSource() .getCodeSource()
@@ -61,14 +60,14 @@ public class JarPathUtil {
} }
// Location 2: ./build/libs/ (development build) // Location 2: ./build/libs/ (development build)
possibleLocations[1] = Paths.get("build", "libs", "restart-helper.jar").toAbsolutePath(); possibleLocations[1] = Path.of("build", "libs", "restart-helper.jar").toAbsolutePath();
// Location 3: app/common/build/libs/ (multi-module build) // Location 3: app/common/build/libs/ (multi-module build)
possibleLocations[2] = possibleLocations[2] =
Paths.get("app", "common", "build", "libs", "restart-helper.jar").toAbsolutePath(); Path.of("app", "common", "build", "libs", "restart-helper.jar").toAbsolutePath();
// Location 4: Current working directory // Location 4: Current working directory
possibleLocations[3] = Paths.get("restart-helper.jar").toAbsolutePath(); possibleLocations[3] = Path.of("restart-helper.jar").toAbsolutePath();
// Check each location // Check each location
for (Path location : possibleLocations) { for (Path location : possibleLocations) {
@@ -2,7 +2,7 @@ package stirling.software.common.model;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import java.nio.file.Paths; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -33,16 +33,15 @@ class ApplicationPropertiesLogicTest {
@Test @Test
void tempFileManagement_defaults_and_overrides() { void tempFileManagement_defaults_and_overrides() {
Function<String, String> normalize = s -> Paths.get(s).normalize().toString(); Function<String, String> normalize = s -> Path.of(s).normalize().toString();
ApplicationProperties.TempFileManagement tfm = ApplicationProperties.TempFileManagement tfm =
new ApplicationProperties.TempFileManagement(); new ApplicationProperties.TempFileManagement();
String expectedBase = String expectedBase =
Paths.get(java.lang.System.getProperty("java.io.tmpdir"), "stirling-pdf") Path.of(java.lang.System.getProperty("java.io.tmpdir"), "stirling-pdf").toString();
.toString();
assertEquals(expectedBase, tfm.getBaseTmpDir()); assertEquals(expectedBase, tfm.getBaseTmpDir());
String expectedLibre = Paths.get(expectedBase, "libreoffice").toString(); String expectedLibre = Path.of(expectedBase, "libreoffice").toString();
assertEquals(expectedLibre, tfm.getLibreofficeDir()); assertEquals(expectedLibre, tfm.getLibreofficeDir());
tfm.setBaseTmpDir("/custom/base"); tfm.setBaseTmpDir("/custom/base");
@@ -4,7 +4,6 @@ import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -75,7 +74,7 @@ public class SPDFApplication {
Map<String, String> propertyFiles = new HashMap<>(); Map<String, String> propertyFiles = new HashMap<>();
// External config files // External config files
Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath()); Path settingsPath = Path.of(InstallationPathConfig.getSettingsPath());
log.info("Settings file: {}", settingsPath.toString()); log.info("Settings file: {}", settingsPath.toString());
if (Files.exists(settingsPath)) { if (Files.exists(settingsPath)) {
propertyFiles.put( propertyFiles.put(
@@ -84,7 +83,7 @@ public class SPDFApplication {
log.warn("External configuration file '{}' does not exist.", settingsPath.toString()); log.warn("External configuration file '{}' does not exist.", settingsPath.toString());
} }
Path customSettingsPath = Paths.get(InstallationPathConfig.getCustomSettingsPath()); Path customSettingsPath = Path.of(InstallationPathConfig.getCustomSettingsPath());
log.info("Custom settings file: {}", customSettingsPath.toString()); log.info("Custom settings file: {}", customSettingsPath.toString());
if (Files.exists(customSettingsPath)) { if (Files.exists(customSettingsPath)) {
String existingLocation = String existingLocation =
@@ -95,7 +95,7 @@ public class ExternalAppDepConfig {
checkDependencyAndDisableGroup(cmd); checkDependencyAndDisableGroup(cmd);
return null; return null;
}) })
.collect(Collectors.toList()); .toList();
invokeAllWithTimeout(tasks, DEFAULT_TIMEOUT.plusSeconds(3)); invokeAllWithTimeout(tasks, DEFAULT_TIMEOUT.plusSeconds(3));
// Python / OpenCV special handling // Python / OpenCV special handling
@@ -5,7 +5,6 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*; import java.util.*;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -115,7 +114,7 @@ public class UIDataController {
if (new java.io.File(runtimePathConfig.getPipelineDefaultWebUiConfigs()).exists()) { if (new java.io.File(runtimePathConfig.getPipelineDefaultWebUiConfigs()).exists()) {
try (Stream<Path> paths = try (Stream<Path> paths =
Files.walk(Paths.get(runtimePathConfig.getPipelineDefaultWebUiConfigs()))) { Files.walk(Path.of(runtimePathConfig.getPipelineDefaultWebUiConfigs()))) {
List<Path> jsonFiles = List<Path> jsonFiles =
paths.filter(Files::isRegularFile) paths.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".json")) .filter(p -> p.toString().endsWith(".json"))
@@ -9,7 +9,6 @@ import java.awt.print.PrinterJob;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
@@ -54,7 +53,7 @@ public class PrintFileController {
MultipartFile file = request.getFileInput(); MultipartFile file = request.getFileInput();
String originalFilename = file.getOriginalFilename(); String originalFilename = file.getOriginalFilename();
if (originalFilename != null if (originalFilename != null
&& (originalFilename.contains("..") || Paths.get(originalFilename).isAbsolute())) { && (originalFilename.contains("..") || Path.of(originalFilename).isAbsolute())) {
throw ExceptionUtils.createIllegalArgumentException( throw ExceptionUtils.createIllegalArgumentException(
"error.invalid.filepath", "Invalid file path detected: " + originalFilename); "error.invalid.filepath", "Invalid file path detected: " + originalFilename);
} }
@@ -7,7 +7,6 @@ import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
@@ -82,7 +81,7 @@ public class PipelineDirectoryProcessor {
try { try {
for (String watchedFoldersDir : watchedFoldersDirs) { for (String watchedFoldersDir : watchedFoldersDirs) {
scanWatchedFolder(Paths.get(watchedFoldersDir).toAbsolutePath()); scanWatchedFolder(Path.of(watchedFoldersDir).toAbsolutePath());
} }
} finally { } finally {
// Clean up ThreadLocal to prevent memory leaks // Clean up ThreadLocal to prevent memory leaks
@@ -442,7 +441,7 @@ public class PipelineDirectoryProcessor {
.replace("{outputFolder}", finishedFoldersDir) .replace("{outputFolder}", finishedFoldersDir)
.replace("{folderName}", dir.toString())) .replace("{folderName}", dir.toString()))
.replaceAll(""); .replaceAll("");
return Paths.get(outputDir).isAbsolute() ? Paths.get(outputDir) : Paths.get(".", outputDir); return Path.of(outputDir).isAbsolute() ? Path.of(outputDir) : Path.of(".", outputDir);
} }
private void deleteOriginalFiles(List<File> filesToProcess, Path processingDir) private void deleteOriginalFiles(List<File> filesToProcess, Path processingDir)
@@ -5,7 +5,6 @@ import java.net.URLDecoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -326,12 +325,12 @@ public class PipelineProcessor {
} }
List<Resource> outputFiles = new ArrayList<>(); List<Resource> outputFiles = new ArrayList<>();
for (File file : files) { for (File file : files) {
Path normalizedPath = Paths.get(file.getName()).normalize(); Path normalizedPath = Path.of(file.getName()).normalize();
if (normalizedPath.startsWith("..")) { if (normalizedPath.startsWith("..")) {
throw new SecurityException( throw new SecurityException(
"Potential path traversal attempt in file name: " + file.getName()); "Potential path traversal attempt in file name: " + file.getName());
} }
Path path = Paths.get(file.getAbsolutePath()); Path path = Path.of(file.getAbsolutePath());
// debug statement // debug statement
log.info("Reading file: {}", path); log.info("Reading file: {}", path);
if (Files.exists(path)) { if (Files.exists(path)) {
@@ -5,7 +5,6 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -66,7 +65,7 @@ public class ReactRoutingController {
} }
// Check for external index.html first (customFiles/static/) // Check for external index.html first (customFiles/static/)
Path externalIndexPath = Paths.get(InstallationPathConfig.getStaticPath(), "index.html"); Path externalIndexPath = Path.of(InstallationPathConfig.getStaticPath(), "index.html");
log.debug("Checking for custom index.html at: {}", externalIndexPath); log.debug("Checking for custom index.html at: {}", externalIndexPath);
if (Files.exists(externalIndexPath) && Files.isReadable(externalIndexPath)) { if (Files.exists(externalIndexPath) && Files.isReadable(externalIndexPath)) {
log.info("Using custom index.html from: {}", externalIndexPath); log.info("Using custom index.html from: {}", externalIndexPath);
@@ -136,7 +135,7 @@ public class ReactRoutingController {
private Resource getIndexHtmlResource() { private Resource getIndexHtmlResource() {
// Check external location first // Check external location first
Path externalIndexPath = Paths.get(InstallationPathConfig.getStaticPath(), "index.html"); Path externalIndexPath = Path.of(InstallationPathConfig.getStaticPath(), "index.html");
if (Files.exists(externalIndexPath) && Files.isReadable(externalIndexPath)) { if (Files.exists(externalIndexPath) && Files.isReadable(externalIndexPath)) {
return new FileSystemResource(externalIndexPath.toFile()); return new FileSystemResource(externalIndexPath.toFile());
} }
@@ -588,7 +588,7 @@ public class PdfJsonConversionService {
.replaceAll(""); .replaceAll("");
return String.format("%s (%s)", cleanName, subtype); return String.format("%s (%s)", cleanName, subtype);
}) })
.collect(java.util.stream.Collectors.toList()); .toList();
long type3Fonts = long type3Fonts =
responseFonts.stream().filter(f -> "Type3".equals(f.getSubtype())).count(); responseFonts.stream().filter(f -> "Type3".equals(f.getSubtype())).count();
@@ -1554,7 +1554,7 @@ public class PdfJsonConversionService {
.glyphName(outline.getGlyphName()) .glyphName(outline.getGlyphName())
.unicode(outline.getUnicode()) .unicode(outline.getUnicode())
.build()) .build())
.collect(Collectors.toList()); .toList();
} }
} catch (Exception ex) { } catch (Exception ex) {
log.debug( log.debug(
@@ -4,7 +4,6 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
@@ -41,8 +40,8 @@ public class SharedSignatureService {
public boolean hasAccessToFile(String username, String fileName) throws IOException { public boolean hasAccessToFile(String username, String fileName) throws IOException {
validateFileName(fileName); validateFileName(fileName);
// Check if file exists in user's personal folder or ALL_USERS folder // Check if file exists in user's personal folder or ALL_USERS folder
Path userPath = Paths.get(SIGNATURE_BASE_PATH, username, fileName); Path userPath = Path.of(SIGNATURE_BASE_PATH, username, fileName);
Path allUsersPath = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER, fileName); Path allUsersPath = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER, fileName);
return Files.exists(userPath) || Files.exists(allUsersPath); return Files.exists(userPath) || Files.exists(allUsersPath);
} }
@@ -52,7 +51,7 @@ public class SharedSignatureService {
// Get signatures from user's personal folder // Get signatures from user's personal folder
if (StringUtils.hasText(username)) { if (StringUtils.hasText(username)) {
Path userFolder = Paths.get(SIGNATURE_BASE_PATH, username); Path userFolder = Path.of(SIGNATURE_BASE_PATH, username);
if (Files.exists(userFolder)) { if (Files.exists(userFolder)) {
try { try {
signatures.addAll(getSignaturesFromFolder(userFolder, "Personal")); signatures.addAll(getSignaturesFromFolder(userFolder, "Personal"));
@@ -63,7 +62,7 @@ public class SharedSignatureService {
} }
// Get signatures from ALL_USERS folder // Get signatures from ALL_USERS folder
Path allUsersFolder = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER); Path allUsersFolder = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER);
if (Files.exists(allUsersFolder)) { if (Files.exists(allUsersFolder)) {
try { try {
signatures.addAll(getSignaturesFromFolder(allUsersFolder, "Shared")); signatures.addAll(getSignaturesFromFolder(allUsersFolder, "Shared"));
@@ -90,7 +89,7 @@ public class SharedSignatureService {
*/ */
public byte[] getSharedSignatureBytes(String fileName) throws IOException { public byte[] getSharedSignatureBytes(String fileName) throws IOException {
validateFileName(fileName); validateFileName(fileName);
Path allUsersPath = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER, fileName); Path allUsersPath = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER, fileName);
if (!Files.exists(allUsersPath)) { if (!Files.exists(allUsersPath)) {
throw new FileNotFoundException("Shared signature file not found"); throw new FileNotFoundException("Shared signature file not found");
} }
@@ -142,7 +141,7 @@ public class SharedSignatureService {
} }
String folderName = "shared".equals(scope) ? ALL_USERS_FOLDER : username; String folderName = "shared".equals(scope) ? ALL_USERS_FOLDER : username;
Path targetFolder = Paths.get(SIGNATURE_BASE_PATH, folderName); Path targetFolder = Path.of(SIGNATURE_BASE_PATH, folderName);
Files.createDirectories(targetFolder); Files.createDirectories(targetFolder);
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
@@ -193,7 +192,7 @@ public class SharedSignatureService {
List<SavedSignatureResponse> signatures = new ArrayList<>(); List<SavedSignatureResponse> signatures = new ArrayList<>();
// Load personal signatures // Load personal signatures
Path personalFolder = Paths.get(SIGNATURE_BASE_PATH, username); Path personalFolder = Path.of(SIGNATURE_BASE_PATH, username);
if (Files.exists(personalFolder)) { if (Files.exists(personalFolder)) {
try (Stream<Path> stream = Files.list(personalFolder)) { try (Stream<Path> stream = Files.list(personalFolder)) {
stream.filter(this::isImageFile) stream.filter(this::isImageFile)
@@ -224,7 +223,7 @@ public class SharedSignatureService {
} }
// Load shared signatures // Load shared signatures
Path sharedFolder = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER); Path sharedFolder = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER);
if (Files.exists(sharedFolder)) { if (Files.exists(sharedFolder)) {
try (Stream<Path> stream = Files.list(sharedFolder)) { try (Stream<Path> stream = Files.list(sharedFolder)) {
stream.filter(this::isImageFile) stream.filter(this::isImageFile)
@@ -262,7 +261,7 @@ public class SharedSignatureService {
validateFileName(signatureId); validateFileName(signatureId);
// Try to find and delete image file in personal folder // Try to find and delete image file in personal folder
Path personalFolder = Paths.get(SIGNATURE_BASE_PATH, username); Path personalFolder = Path.of(SIGNATURE_BASE_PATH, username);
boolean deleted = false; boolean deleted = false;
if (Files.exists(personalFolder)) { if (Files.exists(personalFolder)) {
@@ -283,7 +282,7 @@ public class SharedSignatureService {
// Try shared folder if not found in personal // Try shared folder if not found in personal
if (!deleted) { if (!deleted) {
Path sharedFolder = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER); Path sharedFolder = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER);
if (Files.exists(sharedFolder)) { if (Files.exists(sharedFolder)) {
try (Stream<Path> stream = Files.list(sharedFolder)) { try (Stream<Path> stream = Files.list(sharedFolder)) {
List<Path> matchingFiles = List<Path> matchingFiles =
@@ -10,7 +10,6 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.font.PDType3Font; import org.apache.pdfbox.pdmodel.font.PDType3Font;
@@ -268,7 +267,7 @@ public class Type3FontLibrary {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(String::trim) .map(String::trim)
.filter(s -> !s.isEmpty()) .filter(s -> !s.isEmpty())
.collect(Collectors.toList()); .toList();
} }
private String normalizeAlias(String alias) { private String normalizeAlias(String alias) {
@@ -3,7 +3,6 @@ package stirling.software.SPDF.service.pdfjson.type3.tool;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -285,9 +284,9 @@ public final class Type3SignatureTool {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
String arg = args[i]; String arg = args[i];
if ("--pdf".equals(arg) && i + 1 < args.length) { if ("--pdf".equals(arg) && i + 1 < args.length) {
pdf = Paths.get(args[++i]); pdf = Path.of(args[++i]);
} else if ("--output".equals(arg) && i + 1 < args.length) { } else if ("--output".equals(arg) && i + 1 < args.length) {
output = Paths.get(args[++i]); output = Path.of(args[++i]);
} else if ("--pretty".equals(arg)) { } else if ("--pretty".equals(arg)) {
pretty = true; pretty = true;
} else if ("--help".equals(arg) || "-h".equals(arg)) { } else if ("--help".equals(arg) || "-h".equals(arg)) {
@@ -8,7 +8,6 @@ import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
@@ -411,7 +410,7 @@ public class TelegramPipelineBot extends TelegramLongPollingBot {
private Path getInboxFolder(Long chatId) throws IOException { private Path getInboxFolder(Long chatId) throws IOException {
Path baseInbox = Path baseInbox =
Paths.get( Path.of(
runtimePathConfig.getPipelineWatchedFoldersPath(), runtimePathConfig.getPipelineWatchedFoldersPath(),
telegramProperties.getPipelineInboxFolder()); telegramProperties.getPipelineInboxFolder());
@@ -445,7 +444,7 @@ public class TelegramPipelineBot extends TelegramLongPollingBot {
private List<Path> waitForPipelineOutputs(PipelineFileInfo info) throws IOException { private List<Path> waitForPipelineOutputs(PipelineFileInfo info) throws IOException {
Path finishedDir = Paths.get(runtimePathConfig.getPipelineFinishedFoldersPath()); Path finishedDir = Path.of(runtimePathConfig.getPipelineFinishedFoldersPath());
Files.createDirectories(finishedDir); Files.createDirectories(finishedDir);
Instant start = info.savedAt(); Instant start = info.savedAt();
@@ -3,7 +3,7 @@ package stirling.software.SPDF.controller.api.misc;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths; import java.nio.file.Path;
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;
@@ -34,9 +34,9 @@ class PrintFileControllerTest {
@Test @Test
void printFile_absolutePath_throwsException() { void printFile_absolutePath_throwsException() {
PrintFileRequest request = new PrintFileRequest(); PrintFileRequest request = new PrintFileRequest();
String absPath = Paths.get("/etc/passwd").toString(); String absPath = Path.of("/etc/passwd").toString();
// Only test on systems where /etc/passwd is absolute // Only test on systems where /etc/passwd is absolute
if (Paths.get(absPath).isAbsolute()) { if (Path.of(absPath).isAbsolute()) {
MockMultipartFile file = MockMultipartFile file =
new MockMultipartFile( new MockMultipartFile(
"fileInput", absPath, "application/pdf", "data".getBytes()); "fileInput", absPath, "application/pdf", "data".getBytes());
@@ -207,16 +207,14 @@ public class AuditDashboardController {
// Include standard enum types in case they're not in the database yet // Include standard enum types in case they're not in the database yet
List<String> enumTypes = List<String> enumTypes =
Arrays.stream(AuditEventType.values()) Arrays.stream(AuditEventType.values()).map(AuditEventType::name).toList();
.map(AuditEventType::name)
.collect(Collectors.toList());
// Combine both sources, remove duplicates, and sort // Combine both sources, remove duplicates, and sort
Set<String> combinedTypes = new HashSet<>(); Set<String> combinedTypes = new HashSet<>();
combinedTypes.addAll(dbTypes); combinedTypes.addAll(dbTypes);
combinedTypes.addAll(enumTypes); combinedTypes.addAll(enumTypes);
return combinedTypes.stream().sorted().collect(Collectors.toList()); return combinedTypes.stream().sorted().toList();
} }
/** Export audit data as CSV. */ /** Export audit data as CSV. */
@@ -118,7 +118,7 @@ public class AuditRestController {
// Convert to response format expected by frontend // Convert to response format expected by frontend
List<AuditEventDto> eventDtos = List<AuditEventDto> eventDtos =
events.getContent().stream().map(this::convertToDto).collect(Collectors.toList()); events.getContent().stream().map(this::convertToDto).toList();
AuditEventsResponse response = AuditEventsResponse response =
AuditEventsResponse.builder() AuditEventsResponse.builder()
@@ -191,19 +191,13 @@ public class AuditRestController {
ChartData eventsByTypeChart = ChartData eventsByTypeChart =
ChartData.builder() ChartData.builder()
.labels(new ArrayList<>(eventsByType.keySet())) .labels(new ArrayList<>(eventsByType.keySet()))
.values( .values(eventsByType.values().stream().map(Long::intValue).toList())
eventsByType.values().stream()
.map(Long::intValue)
.collect(Collectors.toList()))
.build(); .build();
ChartData eventsByUserChart = ChartData eventsByUserChart =
ChartData.builder() ChartData.builder()
.labels(new ArrayList<>(eventsByUser.keySet())) .labels(new ArrayList<>(eventsByUser.keySet()))
.values( .values(eventsByUser.values().stream().map(Long::intValue).toList())
eventsByUser.values().stream()
.map(Long::intValue)
.collect(Collectors.toList()))
.build(); .build();
// Sort events by day for time series // Sort events by day for time series
@@ -211,10 +205,7 @@ public class AuditRestController {
ChartData eventsOverTimeChart = ChartData eventsOverTimeChart =
ChartData.builder() ChartData.builder()
.labels(new ArrayList<>(sortedEventsByDay.keySet())) .labels(new ArrayList<>(sortedEventsByDay.keySet()))
.values( .values(sortedEventsByDay.values().stream().map(Long::intValue).toList())
sortedEventsByDay.values().stream()
.map(Long::intValue)
.collect(Collectors.toList()))
.build(); .build();
AuditChartsData chartsData = AuditChartsData chartsData =
@@ -239,16 +230,14 @@ public class AuditRestController {
// Include standard enum types in case they're not in the database yet // Include standard enum types in case they're not in the database yet
List<String> enumTypes = List<String> enumTypes =
Arrays.stream(AuditEventType.values()) Arrays.stream(AuditEventType.values()).map(AuditEventType::name).toList();
.map(AuditEventType::name)
.collect(Collectors.toList());
// Combine both sources, remove duplicates, and sort // Combine both sources, remove duplicates, and sort
Set<String> combinedTypes = new HashSet<>(); Set<String> combinedTypes = new HashSet<>();
combinedTypes.addAll(dbTypes); combinedTypes.addAll(dbTypes);
combinedTypes.addAll(enumTypes); combinedTypes.addAll(enumTypes);
List<String> result = combinedTypes.stream().sorted().collect(Collectors.toList()); List<String> result = combinedTypes.stream().sorted().toList();
return ResponseEntity.ok(result); return ResponseEntity.ok(result);
} }
@@ -263,11 +252,7 @@ public class AuditRestController {
// Use the countByPrincipal query to get unique principals // Use the countByPrincipal query to get unique principals
List<Object[]> principalCounts = auditRepository.countByPrincipal(); List<Object[]> principalCounts = auditRepository.countByPrincipal();
List<String> users = List<String> users = principalCounts.stream().map(arr -> (String) arr[0]).sorted().toList();
principalCounts.stream()
.map(arr -> (String) arr[0])
.sorted()
.collect(Collectors.toList());
return ResponseEntity.ok(users); return ResponseEntity.ok(users);
} }
@@ -3,7 +3,6 @@ package stirling.software.proprietary.controller.api;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -183,7 +182,7 @@ public class SignatureController {
*/ */
private boolean deleteFromSharedFolder(String signatureId) throws IOException { private boolean deleteFromSharedFolder(String signatureId) throws IOException {
String signatureBasePath = InstallationPathConfig.getSignaturesPath(); String signatureBasePath = InstallationPathConfig.getSignaturesPath();
Path sharedFolder = Paths.get(signatureBasePath, ALL_USERS_FOLDER); Path sharedFolder = Path.of(signatureBasePath, ALL_USERS_FOLDER);
boolean deleted = false; boolean deleted = false;
if (Files.exists(sharedFolder)) { if (Files.exists(sharedFolder)) {
@@ -3,7 +3,6 @@ package stirling.software.proprietary.controller.api;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@@ -85,7 +84,7 @@ public class UsageRestController {
.build(); .build();
}) })
.sorted(Comparator.comparingInt(EndpointStatistic::getVisits).reversed()) .sorted(Comparator.comparingInt(EndpointStatistic::getVisits).reversed())
.collect(Collectors.toList()); .toList();
// Apply limit if specified // Apply limit if specified
if (limit != null && limit > 0 && statistics.size() > limit) { if (limit != null && limit > 0 && statistics.size() > limit) {
@@ -3,7 +3,6 @@ package stirling.software.proprietary.security.configuration.ee;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@@ -104,7 +103,7 @@ public class LicenseKeyChecker {
if (keyOrFilePath.startsWith(FILE_PREFIX)) { if (keyOrFilePath.startsWith(FILE_PREFIX)) {
String filePath = keyOrFilePath.substring(FILE_PREFIX.length()); String filePath = keyOrFilePath.substring(FILE_PREFIX.length());
try { try {
Path path = Paths.get(filePath); Path path = Path.of(filePath);
if (!Files.exists(path)) { if (!Files.exists(path)) {
log.error("License file does not exist: {}", filePath); log.error("License file does not exist: {}", filePath);
return null; return null;
@@ -4,7 +4,6 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -329,7 +328,7 @@ public class AdminLicenseController {
} }
// Get config directory and target path // Get config directory and target path
Path configPath = Paths.get(InstallationPathConfig.getConfigPath()); Path configPath = Path.of(InstallationPathConfig.getConfigPath());
Path configPathAbs = configPath.toAbsolutePath().normalize(); Path configPathAbs = configPath.toAbsolutePath().normalize();
Path targetPath = configPathAbs.resolve(filename).normalize(); Path targetPath = configPathAbs.resolve(filename).normalize();
log.info( log.info(
@@ -702,7 +702,7 @@ public class AdminSettingsController {
if (path != null && !path.trim().isEmpty()) { if (path != null && !path.trim().isEmpty()) {
try { try {
java.nio.file.Path normalized = java.nio.file.Path normalized =
java.nio.file.Paths.get(path.trim()).toAbsolutePath().normalize(); java.nio.file.Path.of(path.trim()).toAbsolutePath().normalize();
String normalizedStr = normalized.toString(); String normalizedStr = normalized.toString();
// Check for duplicates // Check for duplicates
@@ -719,9 +719,9 @@ public class AdminSettingsController {
// Check for overlapping paths // Check for overlapping paths
java.util.List<String> pathList = new java.util.ArrayList<>(normalizedPaths); java.util.List<String> pathList = new java.util.ArrayList<>(normalizedPaths);
for (int i = 0; i < pathList.size(); i++) { for (int i = 0; i < pathList.size(); i++) {
java.nio.file.Path path1 = java.nio.file.Paths.get(pathList.get(i)); java.nio.file.Path path1 = java.nio.file.Path.of(pathList.get(i));
for (int j = i + 1; j < pathList.size(); j++) { for (int j = i + 1; j < pathList.size(); j++) {
java.nio.file.Path path2 = java.nio.file.Paths.get(pathList.get(j)); java.nio.file.Path path2 = java.nio.file.Path.of(pathList.get(j));
if (path1.startsWith(path2) || path2.startsWith(path1)) { if (path1.startsWith(path2) || path2.startsWith(path1)) {
return "Overlapping paths detected: " + path1 + " and " + path2; return "Overlapping paths detected: " + path1 + " and " + path2;
} }
@@ -3,7 +3,6 @@ package stirling.software.proprietary.security.controller.api;
import java.security.Principal; import java.security.Principal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -280,7 +279,7 @@ public class InviteLinkController {
"expiresAt", invite.getExpiresAt().toString()); "expiresAt", invite.getExpiresAt().toString());
return inviteMap; return inviteMap;
}) })
.collect(Collectors.toList()); .toList();
return ResponseEntity.ok(Map.of("invites", inviteList)); return ResponseEntity.ok(Map.of("invites", inviteList));
@@ -331,7 +330,7 @@ public class InviteLinkController {
List<InviteToken> expiredInvites = List<InviteToken> expiredInvites =
inviteTokenRepository.findAll().stream() inviteTokenRepository.findAll().stream()
.filter(invite -> !invite.isValid()) .filter(invite -> !invite.isValid())
.collect(Collectors.toList()); .toList();
int count = expiredInvites.size(); int count = expiredInvites.size();
inviteTokenRepository.deleteAll(expiredInvites); inviteTokenRepository.deleteAll(expiredInvites);
@@ -6,7 +6,6 @@ import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -53,7 +52,7 @@ public class UIDataTessdataController {
TessdataLanguagesResponse response = new TessdataLanguagesResponse(); TessdataLanguagesResponse response = new TessdataLanguagesResponse();
response.setInstalled(getAvailableTesseractLanguages()); response.setInstalled(getAvailableTesseractLanguages());
response.setAvailable(getRemoteTessdataLanguages()); response.setAvailable(getRemoteTessdataLanguages());
response.setWritable(isWritableDirectory(Paths.get(runtimePathConfig.getTessDataPath()))); response.setWritable(isWritableDirectory(Path.of(runtimePathConfig.getTessDataPath())));
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} }
@@ -67,7 +66,7 @@ public class UIDataTessdataController {
.body(Map.of("message", "No languages provided for download")); .body(Map.of("message", "No languages provided for download"));
} }
Path tessdataDir = Paths.get(runtimePathConfig.getTessDataPath()); Path tessdataDir = Path.of(runtimePathConfig.getTessDataPath());
try { try {
Files.createDirectories(tessdataDir); Files.createDirectories(tessdataDir);
} catch (IOException e) { } catch (IOException e) {
@@ -994,7 +994,7 @@ public class UserController {
userRepository.findAll().stream() userRepository.findAll().stream()
.filter(User::isEnabled) .filter(User::isEnabled)
.map(this::toUserSummaryDTO) .map(this::toUserSummaryDTO)
.collect(java.util.stream.Collectors.toList()); .toList();
return ResponseEntity.ok(users); return ResponseEntity.ok(users);
} }
@@ -4,7 +4,6 @@ import java.io.IOException;
import java.nio.file.DirectoryStream; import java.nio.file.DirectoryStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest; import java.security.MessageDigest;
@@ -23,7 +22,6 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.sql.DataSource; import javax.sql.DataSource;
@@ -100,7 +98,7 @@ public class DatabaseService implements DatabaseServiceInterface {
ApplicationProperties.Datasource datasourceProps, ApplicationProperties.Datasource datasourceProps,
DataSource dataSource, DataSource dataSource,
DatabaseNotificationServiceInterface backupNotificationService) { DatabaseNotificationServiceInterface backupNotificationService) {
this.BACKUP_DIR = Paths.get(InstallationPathConfig.getBackupPath()).normalize(); this.BACKUP_DIR = Path.of(InstallationPathConfig.getBackupPath()).normalize();
this.datasourceProps = datasourceProps; this.datasourceProps = datasourceProps;
this.dataSource = dataSource; this.dataSource = dataSource;
this.backupNotificationService = backupNotificationService; this.backupNotificationService = backupNotificationService;
@@ -111,7 +109,7 @@ public class DatabaseService implements DatabaseServiceInterface {
@Deprecated(since = "2.0.0", forRemoval = true) @Deprecated(since = "2.0.0", forRemoval = true)
private void moveBackupFiles() { private void moveBackupFiles() {
Path sourceDir = Path sourceDir =
Paths.get(InstallationPathConfig.getConfigPath(), "db", "backup").normalize(); Path.of(InstallationPathConfig.getConfigPath(), "db", "backup").normalize();
if (!Files.exists(sourceDir)) { if (!Files.exists(sourceDir)) {
log.info("Source directory does not exist: {}", sourceDir); log.info("Source directory does not exist: {}", sourceDir);
@@ -217,7 +215,7 @@ public class DatabaseService implements DatabaseServiceInterface {
List<FileInfo> backupList = this.getBackupList(); List<FileInfo> backupList = this.getBackupList();
backupList.sort(Comparator.comparing(FileInfo::getModificationDate).reversed()); backupList.sort(Comparator.comparing(FileInfo::getModificationDate).reversed());
Path latestExport = Paths.get(backupList.get(0).getFilePath()); Path latestExport = Path.of(backupList.get(0).getFilePath());
executeDatabaseScript(latestExport); executeDatabaseScript(latestExport);
} }
@@ -258,9 +256,13 @@ public class DatabaseService implements DatabaseServiceInterface {
@Override @Override
public void exportDatabase() { public void exportDatabase() {
List<FileInfo> filteredBackupList = List<FileInfo> filteredBackupList =
this.getBackupList().stream() new ArrayList<>(
.filter(backup -> !backup.getFileName().startsWith(BACKUP_PREFIX + "user_")) this.getBackupList().stream()
.collect(Collectors.toList()); .filter(
backup ->
!backup.getFileName()
.startsWith(BACKUP_PREFIX + "user_"))
.toList());
if (filteredBackupList.size() > 5) { if (filteredBackupList.size() > 5) {
deleteOldestBackup(filteredBackupList); deleteOldestBackup(filteredBackupList);
@@ -341,7 +343,7 @@ public class DatabaseService implements DatabaseServiceInterface {
for (FileInfo backup : backupList) { for (FileInfo backup : backupList) {
try { try {
Files.deleteIfExists(Paths.get(backup.getFilePath())); Files.deleteIfExists(Path.of(backup.getFilePath()));
deletedFiles.add(Pair.of(backup, true)); deletedFiles.add(Pair.of(backup, true));
} catch (IOException e) { } catch (IOException e) {
log.error("Error deleting backup file: {}", backup.getFileName(), e); log.error("Error deleting backup file: {}", backup.getFileName(), e);
@@ -359,7 +361,7 @@ public class DatabaseService implements DatabaseServiceInterface {
if (!backupList.isEmpty()) { if (!backupList.isEmpty()) {
FileInfo lastBackup = backupList.get(backupList.size() - 1); FileInfo lastBackup = backupList.get(backupList.size() - 1);
try { try {
Files.deleteIfExists(Paths.get(lastBackup.getFilePath())); Files.deleteIfExists(Path.of(lastBackup.getFilePath()));
deletedFiles.add(Pair.of(lastBackup, true)); deletedFiles.add(Pair.of(lastBackup, true));
} catch (IOException e) { } catch (IOException e) {
log.error("Error deleting last backup file: {}", lastBackup.getFileName(), e); log.error("Error deleting last backup file: {}", lastBackup.getFileName(), e);
@@ -381,7 +383,7 @@ public class DatabaseService implements DatabaseServiceInterface {
p -> p.getFileName().substring(7, p.getFileName().length() - 4))); p -> p.getFileName().substring(7, p.getFileName().length() - 4)));
FileInfo oldestFile = filteredBackupList.get(0); FileInfo oldestFile = filteredBackupList.get(0);
Files.deleteIfExists(Paths.get(oldestFile.getFilePath())); Files.deleteIfExists(Path.of(oldestFile.getFilePath()));
log.info("Deleted oldest backup: {}", oldestFile.getFileName()); log.info("Deleted oldest backup: {}", oldestFile.getFileName());
} catch (IOException e) { } catch (IOException e) {
log.error("Unable to delete oldest backup, message: {}", e.getMessage(), e); log.error("Unable to delete oldest backup, message: {}", e.getMessage(), e);
@@ -3,7 +3,6 @@ package stirling.software.proprietary.security.service;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -76,7 +75,7 @@ public class KeyPairCleanupService {
return; return;
} }
Path privateKeyDirectory = Paths.get(InstallationPathConfig.getPrivateKeyPath()); Path privateKeyDirectory = Path.of(InstallationPathConfig.getPrivateKeyPath());
Path keyFile = privateKeyDirectory.resolve(keyId + KeyPersistenceService.KEY_SUFFIX); Path keyFile = privateKeyDirectory.resolve(keyId + KeyPersistenceService.KEY_SUFFIX);
if (Files.exists(keyFile)) { if (Files.exists(keyFile)) {
@@ -3,7 +3,6 @@ package stirling.software.proprietary.security.service;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
@@ -20,7 +19,6 @@ import java.time.format.DateTimeFormatter;
import java.util.Base64; import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
@@ -82,7 +80,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
*/ */
private void loadExistingKeysFromDisk() { private void loadExistingKeysFromDisk() {
try { try {
Path keyDirectory = Paths.get(InstallationPathConfig.getPrivateKeyPath()); Path keyDirectory = Path.of(InstallationPathConfig.getPrivateKeyPath());
if (!Files.exists(keyDirectory)) { if (!Files.exists(keyDirectory)) {
log.info("No existing keys found, generating new keypair"); log.info("No existing keys found, generating new keypair");
@@ -99,7 +97,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
b.getFileName().compareTo(a.getFileName())) // Most b.getFileName().compareTo(a.getFileName())) // Most
// recent // recent
// first // first
.collect(Collectors.toList()); .toList();
} }
if (keyFiles.isEmpty()) { if (keyFiles.isEmpty()) {
@@ -275,7 +273,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
eligible); eligible);
return eligible; return eligible;
}) })
.collect(Collectors.toList()); .toList();
} }
private String generateKeyId() { private String generateKeyId() {
@@ -284,20 +282,17 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
} }
private KeyPair generateRSAKeypair() { private KeyPair generateRSAKeypair() {
KeyPairGenerator keyPairGenerator = null;
try { try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048); keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
log.error("Failed to initialize RSA key pair generator", e); throw new IllegalStateException("RSA key pair generator is not available", e);
} }
return keyPairGenerator.generateKeyPair();
} }
private void ensurePrivateKeyDirectoryExists() throws IOException { private void ensurePrivateKeyDirectoryExists() throws IOException {
Path keyPath = Paths.get(InstallationPathConfig.getPrivateKeyPath()); Path keyPath = Path.of(InstallationPathConfig.getPrivateKeyPath());
if (!Files.exists(keyPath)) { if (!Files.exists(keyPath)) {
Files.createDirectories(keyPath); Files.createDirectories(keyPath);
@@ -312,7 +307,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
* <p>Public key stored as: keyId.pub * <p>Public key stored as: keyId.pub
*/ */
private void storeKeyPair(String keyId, KeyPair keyPair) throws IOException { private void storeKeyPair(String keyId, KeyPair keyPair) throws IOException {
Path keyDirectory = Paths.get(InstallationPathConfig.getPrivateKeyPath()); Path keyDirectory = Path.of(InstallationPathConfig.getPrivateKeyPath());
// Store private key // Store private key
Path privateKeyFile = keyDirectory.resolve(keyId + KEY_SUFFIX); Path privateKeyFile = keyDirectory.resolve(keyId + KEY_SUFFIX);
@@ -346,7 +341,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
private PrivateKey loadPrivateKey(String keyId) private PrivateKey loadPrivateKey(String keyId)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Path keyFile = Path keyFile =
Paths.get(InstallationPathConfig.getPrivateKeyPath()).resolve(keyId + KEY_SUFFIX); Path.of(InstallationPathConfig.getPrivateKeyPath()).resolve(keyId + KEY_SUFFIX);
if (!Files.exists(keyFile)) { if (!Files.exists(keyFile)) {
throw new IOException("Private key not found: " + keyFile); throw new IOException("Private key not found: " + keyFile);
@@ -369,8 +364,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
*/ */
private String loadPublicKey(String keyId) throws IOException { private String loadPublicKey(String keyId) throws IOException {
Path publicKeyFile = Path publicKeyFile =
Paths.get(InstallationPathConfig.getPrivateKeyPath()) Path.of(InstallationPathConfig.getPrivateKeyPath()).resolve(keyId + PUB_KEY_SUFFIX);
.resolve(keyId + PUB_KEY_SUFFIX);
if (!Files.exists(publicKeyFile)) { if (!Files.exists(publicKeyFile)) {
throw new IOException("Public key not found: " + publicKeyFile); throw new IOException("Public key not found: " + publicKeyFile);
@@ -5,7 +5,6 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -101,7 +100,7 @@ public class LoginAttemptService {
return attemptsCache.entrySet().stream() return attemptsCache.entrySet().stream()
.filter(entry -> entry.getValue().getAttemptCount() >= MAX_ATTEMPT) .filter(entry -> entry.getValue().getAttemptCount() >= MAX_ATTEMPT)
.map(Map.Entry::getKey) .map(Map.Entry::getKey)
.collect(Collectors.toList()); .toList();
} }
public int getRemainingAttempts(String key) { public int getRemainingAttempts(String key) {
@@ -31,21 +31,14 @@ public class RefreshRateLimitService {
this.jwtProperties = applicationProperties.getSecurity().getJwt(); this.jwtProperties = applicationProperties.getSecurity().getJwt();
} }
private static class RefreshAttempt { private record RefreshAttempt(AtomicInteger count, Instant firstAttempt) {
private final AtomicInteger count = new AtomicInteger(0); RefreshAttempt() {
private final Instant firstAttempt = Instant.now(); this(new AtomicInteger(0), Instant.now());
}
int incrementAndGet() { int incrementAndGet() {
return count.incrementAndGet(); return count.incrementAndGet();
} }
Instant getFirstAttempt() {
return firstAttempt;
}
int getCount() {
return count.get();
}
} }
private final Map<String, RefreshAttempt> attempts = new ConcurrentHashMap<>(); private final Map<String, RefreshAttempt> attempts = new ConcurrentHashMap<>();
@@ -72,7 +65,7 @@ public class RefreshRateLimitService {
// Clean up if outside grace window // Clean up if outside grace window
Instant cutoff = Instant.now().minusMillis(graceWindowMillis); Instant cutoff = Instant.now().minusMillis(graceWindowMillis);
if (attempt.getFirstAttempt().isBefore(cutoff)) { if (attempt.firstAttempt().isBefore(cutoff)) {
attempts.remove(tokenHash); attempts.remove(tokenHash);
} }
@@ -98,15 +91,9 @@ public class RefreshRateLimitService {
? configuredMinutes ? configuredMinutes
: JwtConstants.DEFAULT_REFRESH_GRACE_MINUTES; : JwtConstants.DEFAULT_REFRESH_GRACE_MINUTES;
Instant cutoff = Instant.now().minusMillis(graceMinutes * 60000L); Instant cutoff = Instant.now().minusMillis(graceMinutes * 60000L);
int removed = int beforeSize = attempts.size();
attempts.entrySet().stream() attempts.entrySet().removeIf(entry -> entry.getValue().firstAttempt().isBefore(cutoff));
.filter(entry -> entry.getValue().getFirstAttempt().isBefore(cutoff)) int removed = beforeSize - attempts.size();
.mapToInt(
entry -> {
attempts.remove(entry.getKey());
return 1;
})
.sum();
if (removed > 0) { if (removed > 0) {
log.debug("Cleaned up {} expired refresh tracking entries", removed); log.debug("Cleaned up {} expired refresh tracking entries", removed);
@@ -11,7 +11,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -413,7 +412,7 @@ public class AuditService {
return m; return m;
}) })
.collect(Collectors.toList()); .toList();
data.put("files", fileInfos); data.put("files", fileInfos);
} }
@@ -4,7 +4,6 @@ import java.io.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*; import java.security.*;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
@@ -64,7 +63,7 @@ public class ServerCertificateService implements ServerCertificateServiceInterfa
} }
private Path getKeystorePath() { private Path getKeystorePath() {
return Paths.get(InstallationPathConfig.getConfigPath(), KEYSTORE_FILENAME); return Path.of(InstallationPathConfig.getConfigPath(), KEYSTORE_FILENAME);
} }
private boolean hasProOrEnterpriseAccess() { private boolean hasProOrEnterpriseAccess() {
@@ -5,7 +5,6 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
@@ -55,7 +54,7 @@ public class SignatureService implements PersonalSignatureServiceInterface {
@Override @Override
public byte[] getPersonalSignatureBytes(String username, String fileName) throws IOException { public byte[] getPersonalSignatureBytes(String username, String fileName) throws IOException {
validateFileName(fileName); validateFileName(fileName);
Path userPath = Paths.get(SIGNATURE_BASE_PATH, username, fileName); Path userPath = Path.of(SIGNATURE_BASE_PATH, username, fileName);
if (!Files.exists(userPath)) { if (!Files.exists(userPath)) {
throw new FileNotFoundException("Personal signature not found"); throw new FileNotFoundException("Personal signature not found");
@@ -76,7 +75,7 @@ public class SignatureService implements PersonalSignatureServiceInterface {
} }
String folderName = "shared".equals(scope) ? ALL_USERS_FOLDER : username; String folderName = "shared".equals(scope) ? ALL_USERS_FOLDER : username;
Path targetFolder = Paths.get(SIGNATURE_BASE_PATH, folderName); Path targetFolder = Path.of(SIGNATURE_BASE_PATH, folderName);
// Only enforce limits for personal signatures (not shared) // Only enforce limits for personal signatures (not shared)
if ("personal".equals(scope)) { if ("personal".equals(scope)) {
@@ -170,13 +169,13 @@ public class SignatureService implements PersonalSignatureServiceInterface {
List<SavedSignatureResponse> signatures = new ArrayList<>(); List<SavedSignatureResponse> signatures = new ArrayList<>();
// Load personal signatures // Load personal signatures
Path personalFolder = Paths.get(SIGNATURE_BASE_PATH, username); Path personalFolder = Path.of(SIGNATURE_BASE_PATH, username);
if (Files.exists(personalFolder)) { if (Files.exists(personalFolder)) {
signatures.addAll(loadSignaturesFromFolder(personalFolder, "personal", true)); signatures.addAll(loadSignaturesFromFolder(personalFolder, "personal", true));
} }
// Load shared signatures // Load shared signatures
Path sharedFolder = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER); Path sharedFolder = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER);
if (Files.exists(sharedFolder)) { if (Files.exists(sharedFolder)) {
signatures.addAll(loadSignaturesFromFolder(sharedFolder, "shared", false)); signatures.addAll(loadSignaturesFromFolder(sharedFolder, "shared", false));
} }
@@ -189,7 +188,7 @@ public class SignatureService implements PersonalSignatureServiceInterface {
validateFileName(signatureId); validateFileName(signatureId);
// Only allow deletion from personal folder // Only allow deletion from personal folder
Path personalFolder = Paths.get(SIGNATURE_BASE_PATH, username); Path personalFolder = Path.of(SIGNATURE_BASE_PATH, username);
boolean deleted = false; boolean deleted = false;
if (Files.exists(personalFolder)) { if (Files.exists(personalFolder)) {
@@ -227,7 +226,7 @@ public class SignatureService implements PersonalSignatureServiceInterface {
validateFileName(signatureId); validateFileName(signatureId);
// Try personal folder first // Try personal folder first
Path personalFolder = Paths.get(SIGNATURE_BASE_PATH, username); Path personalFolder = Path.of(SIGNATURE_BASE_PATH, username);
Path metadataPath = personalFolder.resolve(signatureId + ".json"); Path metadataPath = personalFolder.resolve(signatureId + ".json");
if (Files.exists(metadataPath)) { if (Files.exists(metadataPath)) {
@@ -237,7 +236,7 @@ public class SignatureService implements PersonalSignatureServiceInterface {
} }
// If not found in personal, try shared folder // If not found in personal, try shared folder
Path sharedFolder = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER); Path sharedFolder = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER);
Path sharedMetadataPath = sharedFolder.resolve(signatureId + ".json"); Path sharedMetadataPath = sharedFolder.resolve(signatureId + ".json");
if (Files.exists(sharedMetadataPath)) { if (Files.exists(sharedMetadataPath)) {
@@ -251,7 +250,7 @@ public class SignatureService implements PersonalSignatureServiceInterface {
public boolean isSharedSignature(String signatureId) { public boolean isSharedSignature(String signatureId) {
validateFileName(signatureId); validateFileName(signatureId);
Path sharedFolder = Paths.get(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER); Path sharedFolder = Path.of(SIGNATURE_BASE_PATH, ALL_USERS_FOLDER);
return Files.exists(sharedFolder.resolve(signatureId + ".json")); return Files.exists(sharedFolder.resolve(signatureId + ".json"));
} }
@@ -274,7 +273,7 @@ public class SignatureService implements PersonalSignatureServiceInterface {
// Private helper methods // Private helper methods
private void enforceStorageLimits(String username, String dataUrlToAdd) throws IOException { private void enforceStorageLimits(String username, String dataUrlToAdd) throws IOException {
Path userFolder = Paths.get(SIGNATURE_BASE_PATH, username); Path userFolder = Path.of(SIGNATURE_BASE_PATH, username);
if (!Files.exists(userFolder)) { if (!Files.exists(userFolder)) {
return; // First signature, no limits to check return; // First signature, no limits to check
@@ -3,7 +3,6 @@ package stirling.software.proprietary.storage.config;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale; import java.util.Locale;
import java.util.Optional; import java.util.Optional;
@@ -58,8 +57,8 @@ public class StorageProviderConfig {
} }
basePathValue = InstallationPathConfig.getPath() + "storage"; basePathValue = InstallationPathConfig.getPath() + "storage";
} }
Path basePath = Paths.get(basePathValue).toAbsolutePath().normalize(); Path basePath = Path.of(basePathValue).toAbsolutePath().normalize();
Path installRoot = Paths.get(InstallationPathConfig.getPath()).toAbsolutePath().normalize(); Path installRoot = Path.of(InstallationPathConfig.getPath()).toAbsolutePath().normalize();
if (!basePath.startsWith(installRoot)) { if (!basePath.startsWith(installRoot)) {
// Warn rather than hard-fail: admins may legitimately point storage at an external // Warn rather than hard-fail: admins may legitimately point storage at an external
// volume, but an unexpected path could indicate a misconfiguration or traversal // volume, but an unexpected path could indicate a misconfiguration or traversal
@@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@@ -80,7 +79,7 @@ public class LocalStorageProvider implements StorageProvider {
if (filename == null || filename.isBlank()) { if (filename == null || filename.isBlank()) {
return "file"; return "file";
} }
String stripped = Paths.get(filename).getFileName().toString().replaceAll("\\p{Cntrl}", ""); String stripped = Path.of(filename).getFileName().toString().replaceAll("\\p{Cntrl}", "");
return stripped.isBlank() ? "file" : stripped; return stripped.isBlank() ? "file" : stripped;
} }
} }
@@ -4,7 +4,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Paths; import java.nio.file.Path;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@@ -150,7 +150,7 @@ public class S3StorageProvider implements StorageProvider, AutoCloseable {
if (originalFilename == null || originalFilename.isBlank()) { if (originalFilename == null || originalFilename.isBlank()) {
return null; return null;
} }
// Strip CR/LF and other control chars before path parsing (Paths.get throws on them on // Strip CR/LF and other control chars before path parsing (Path.of throws on them on
// Windows, and they defeat header parsers). // Windows, and they defeat header parsers).
String stripped = originalFilename.replaceAll("\\p{Cntrl}", ""); String stripped = originalFilename.replaceAll("\\p{Cntrl}", "");
// Use only the basename to avoid leaking directory structure into the header. // Use only the basename to avoid leaking directory structure into the header.
@@ -184,7 +184,7 @@ public class S3StorageProvider implements StorageProvider, AutoCloseable {
if (filename == null || filename.isBlank()) { if (filename == null || filename.isBlank()) {
return "file"; return "file";
} }
String stripped = Paths.get(filename).getFileName().toString().replaceAll("\\p{Cntrl}", ""); String stripped = Path.of(filename).getFileName().toString().replaceAll("\\p{Cntrl}", "");
return stripped.isBlank() ? "file" : stripped; return stripped.isBlank() ? "file" : stripped;
} }
} }
@@ -14,7 +14,6 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@@ -361,7 +360,7 @@ public class FileStorageService {
return files.stream() return files.stream()
.sorted(Comparator.comparing(StoredFile::getCreatedAt).reversed()) .sorted(Comparator.comparing(StoredFile::getCreatedAt).reversed())
.map(file -> buildResponse(file, user, roleByFileId.get(file.getId()))) .map(file -> buildResponse(file, user, roleByFileId.get(file.getId())))
.collect(Collectors.toList()); .toList();
} }
public StoredFileResponse getAccessibleFileResponse(User user, Long fileId) { public StoredFileResponse getAccessibleFileResponse(User user, Long fileId) {
@@ -400,7 +399,7 @@ public class FileStorageService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(User::getUsername) .map(User::getUsername)
.sorted(String.CASE_INSENSITIVE_ORDER) .sorted(String.CASE_INSENSITIVE_ORDER)
.collect(Collectors.toList()) .toList()
: List.of(); : List.of();
List<ShareLinkResponse> shareLinks = List<ShareLinkResponse> shareLinks =
ownedByCurrentUser && isShareLinksEnabled() ownedByCurrentUser && isShareLinksEnabled()
@@ -419,7 +418,7 @@ public class FileStorageService {
.expiresAt(share.getExpiresAt()) .expiresAt(share.getExpiresAt())
.build()) .build())
.sorted(Comparator.comparing(ShareLinkResponse::getCreatedAt)) .sorted(Comparator.comparing(ShareLinkResponse::getCreatedAt))
.collect(Collectors.toList()) .toList()
: List.of(); : List.of();
List<SharedUserResponse> sharedUsers = List<SharedUserResponse> sharedUsers =
ownedByCurrentUser ownedByCurrentUser
@@ -440,7 +439,7 @@ public class FileStorageService {
Comparator.comparing( Comparator.comparing(
SharedUserResponse::getUsername, SharedUserResponse::getUsername,
String.CASE_INSENSITIVE_ORDER)) String.CASE_INSENSITIVE_ORDER))
.collect(Collectors.toList()) .toList()
: List.of(); : List.of();
return StoredFileResponse.builder() return StoredFileResponse.builder()
.id(file.getId()) .id(file.getId())
@@ -762,7 +761,7 @@ public class FileStorageService {
.accessType(access.getAccessType().name()) .accessType(access.getAccessType().name())
.accessedAt(access.getAccessedAt()) .accessedAt(access.getAccessedAt())
.build()) .build())
.collect(Collectors.toList()); .toList();
} }
public List<FileShareAccess> listAccessedShareLinks(User user) { public List<FileShareAccess> listAccessedShareLinks(User user) {
@@ -818,7 +817,7 @@ public class FileStorageService {
.build(); .build();
}) })
.filter(response -> response.getShareToken() != null) .filter(response -> response.getShareToken() != null)
.collect(Collectors.toList()); .toList();
} }
public void ensureSharingEnabled() { public void ensureSharingEnabled() {
@@ -1007,7 +1006,7 @@ public class FileStorageService {
file.getHistoryStorageKey(), file.getHistoryStorageKey(),
file.getAuditLogStorageKey()) file.getAuditLogStorageKey())
.filter(value -> value != null && !value.isBlank()) .filter(value -> value != null && !value.isBlank())
.collect(Collectors.toList()); .toList();
} }
private void cleanupStoredObject(StoredObject storedObject) { private void cleanupStoredObject(StoredObject storedObject) {
@@ -76,7 +76,7 @@ public class SigningSessionController {
.map( .map(
stirling.software.proprietary.workflow.util.WorkflowMapper stirling.software.proprietary.workflow.util.WorkflowMapper
::toResponse) ::toResponse)
.collect(java.util.stream.Collectors.toList()); .toList();
return ResponseEntity.ok(responses); return ResponseEntity.ok(responses);
} catch (Exception e) { } catch (Exception e) {
log.error("Error listing sessions for user {}", principal.getName(), e); log.error("Error listing sessions for user {}", principal.getName(), e);
@@ -98,7 +98,7 @@ public class SigningFinalizationService {
// Step 1.5: Add summary page BEFORE digital signing (if enabled) // Step 1.5: Add summary page BEFORE digital signing (if enabled)
// CRITICAL: Must be done before signing to avoid invalidating signatures // CRITICAL: Must be done before signing to avoid invalidating signatures
if (Boolean.TRUE.equals(settings.includeSummaryPage)) { if (Boolean.TRUE.equals(settings.includeSummaryPage())) {
log.info( log.info(
"Adding summary page before digital signing for session {}", "Adding summary page before digital signing for session {}",
session.getSessionId()); session.getSessionId());
@@ -108,11 +108,13 @@ public class SigningFinalizationService {
// Suppress digital certificate visual block when summary page is enabled // Suppress digital certificate visual block when summary page is enabled
// (wet signatures already applied in Step 1 and will still appear) // (wet signatures already applied in Step 1 and will still appear)
Boolean showVisualSignature = Boolean showVisualSignature =
Boolean.TRUE.equals(settings.includeSummaryPage) ? false : settings.showSignature; Boolean.TRUE.equals(settings.includeSummaryPage())
? false
: settings.showSignature();
log.info( log.info(
"Finalization settings: includeSummaryPage={}, showVisualSignature={}", "Finalization settings: includeSummaryPage={}, showVisualSignature={}",
settings.includeSummaryPage, settings.includeSummaryPage(),
showVisualSignature); showVisualSignature);
// Step 2: Apply digital certificates per SIGNED participant // Step 2: Apply digital certificates per SIGNED participant
@@ -150,8 +152,8 @@ public class SigningFinalizationService {
log.info( log.info(
"Applying signature for {} with reason='{}', location='{}'", "Applying signature for {} with reason='{}', location='{}'",
fresh.getEmail(), fresh.getEmail(),
sigMeta.reason, sigMeta.reason(),
sigMeta.location); sigMeta.location());
pdf = pdf =
applyDigitalSignature( applyDigitalSignature(
@@ -159,10 +161,10 @@ public class SigningFinalizationService {
fresh, fresh,
submission, submission,
showVisualSignature, showVisualSignature,
settings.pageNumber, settings.pageNumber(),
sigMeta.reason, sigMeta.reason(),
sigMeta.location, sigMeta.location(),
settings.showLogo); settings.showLogo());
} }
return pdf; return pdf;
@@ -682,7 +684,7 @@ public class SigningFinalizationService {
textDark, textDark,
textMuted, textMuted,
"Subject:", "Subject:",
certInfo.subjectCN); certInfo.subjectCN());
rRowY -= LINE_H; rRowY -= LINE_H;
drawLabelValue( drawLabelValue(
cs, cs,
@@ -693,7 +695,7 @@ public class SigningFinalizationService {
textDark, textDark,
textMuted, textMuted,
"Issuer:", "Issuer:",
certInfo.issuerCN); certInfo.issuerCN());
rRowY -= LINE_H; rRowY -= LINE_H;
drawLabelValue( drawLabelValue(
cs, cs,
@@ -704,7 +706,7 @@ public class SigningFinalizationService {
textDark, textDark,
textMuted, textMuted,
"Serial:", "Serial:",
certInfo.serialNumber); certInfo.serialNumber());
rRowY -= LINE_H; rRowY -= LINE_H;
drawLabelValue( drawLabelValue(
cs, cs,
@@ -715,7 +717,7 @@ public class SigningFinalizationService {
textDark, textDark,
textMuted, textMuted,
"Valid From:", "Valid From:",
certInfo.validFrom); certInfo.validFrom());
rRowY -= LINE_H; rRowY -= LINE_H;
drawLabelValue( drawLabelValue(
cs, cs,
@@ -726,7 +728,7 @@ public class SigningFinalizationService {
textDark, textDark,
textMuted, textMuted,
"Valid Until:", "Valid Until:",
certInfo.validUntil); certInfo.validUntil());
rRowY -= LINE_H; rRowY -= LINE_H;
drawLabelValue( drawLabelValue(
cs, cs,
@@ -737,7 +739,7 @@ public class SigningFinalizationService {
textDark, textDark,
textMuted, textMuted,
"Algorithm:", "Algorithm:",
certInfo.algorithm); certInfo.algorithm());
} }
yPos -= cardH + 12; yPos -= cardH + 12;
@@ -1075,10 +1077,9 @@ public class SigningFinalizationService {
} }
String alias = aliases.nextElement(); String alias = aliases.nextElement();
Certificate cert = keystore.getCertificate(alias); Certificate cert = keystore.getCertificate(alias);
if (!(cert instanceof X509Certificate)) { if (!(cert instanceof X509Certificate x509)) {
return null; return null;
} }
X509Certificate x509 = (X509Certificate) cert;
String subjectCN = extractCN(x509.getSubjectX500Principal().getName()); String subjectCN = extractCN(x509.getSubjectX500Principal().getName());
String issuerCN = extractCN(x509.getIssuerX500Principal().getName()); String issuerCN = extractCN(x509.getIssuerX500Principal().getName());
@@ -1256,55 +1257,19 @@ public class SigningFinalizationService {
// ===== PRIVATE INNER TYPES ===== // ===== PRIVATE INNER TYPES =====
private static class SessionSignatureSettings { private record SessionSignatureSettings(
final Boolean showSignature; Boolean showSignature,
final Integer pageNumber; Integer pageNumber,
final Boolean showLogo; Boolean showLogo,
final Boolean includeSummaryPage; Boolean includeSummaryPage) {}
SessionSignatureSettings( private record ParticipantSignatureMetadata(String reason, String location) {}
Boolean showSignature,
Integer pageNumber,
Boolean showLogo,
Boolean includeSummaryPage) {
this.showSignature = showSignature;
this.pageNumber = pageNumber;
this.showLogo = showLogo;
this.includeSummaryPage = includeSummaryPage;
}
}
private static class ParticipantSignatureMetadata { private record CertificateInfo(
final String reason; String subjectCN,
final String location; String issuerCN,
String serialNumber,
ParticipantSignatureMetadata(String reason, String location) { String validFrom,
this.reason = reason; String validUntil,
this.location = location; String algorithm) {}
}
}
private static class CertificateInfo {
final String subjectCN;
final String issuerCN;
final String serialNumber;
final String validFrom;
final String validUntil;
final String algorithm;
CertificateInfo(
String subjectCN,
String issuerCN,
String serialNumber,
String validFrom,
String validUntil,
String algorithm) {
this.subjectCN = subjectCN;
this.issuerCN = issuerCN;
this.serialNumber = serialNumber;
this.validFrom = validFrom;
this.validUntil = validUntil;
this.algorithm = algorithm;
}
}
} }
@@ -553,7 +553,7 @@ public class WorkflowSessionService {
dto.setMyStatus(p.getStatus()); dto.setMyStatus(p.getStatus());
return dto; return dto;
}) })
.collect(java.util.stream.Collectors.toList()); .toList();
} }
/** /**
@@ -3,7 +3,6 @@ package stirling.software.proprietary.workflow.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@@ -80,12 +79,12 @@ public class WorkflowMapper {
response.setParticipants( response.setParticipants(
session.getParticipants().stream() session.getParticipants().stream()
.map(p -> toParticipantResponse(p, objectMapper, includeShareTokens)) .map(p -> toParticipantResponse(p, objectMapper, includeShareTokens))
.collect(Collectors.toList())); .toList());
} else { } else {
response.setParticipants( response.setParticipants(
session.getParticipants().stream() session.getParticipants().stream()
.map(p -> toParticipantResponse(p, includeShareTokens)) .map(p -> toParticipantResponse(p, includeShareTokens))
.collect(Collectors.toList())); .toList());
} }
// Calculate participant counts // Calculate participant counts
+1 -1
View File
@@ -12,7 +12,7 @@ public class PropSync {
File folder = new File("C:\\Users\\systo\\git\\Stirling-PDF\\app\\core\\src\\main\\resources"); File folder = new File("C:\\Users\\systo\\git\\Stirling-PDF\\app\\core\\src\\main\\resources");
File[] files = folder.listFiles((dir, name) -> name.matches("messages_.*\\.properties")); File[] files = folder.listFiles((dir, name) -> name.matches("messages_.*\\.properties"));
List<String> enLines = Files.readAllLines(Paths.get(folder + "\\messages_en_GB.properties"), StandardCharsets.UTF_8); List<String> enLines = Files.readAllLines(Path.of(folder + "\\messages_en_GB.properties"), StandardCharsets.UTF_8);
Map<String, String> enProps = linesToProps(enLines); Map<String, String> enProps = linesToProps(enLines);
for (File file : files) { for (File file : files) {
+2 -2
View File
@@ -19,9 +19,9 @@ public class RestartHelper {
Map<String, String> cli = parseArgs(args); Map<String, String> cli = parseArgs(args);
long pid = Long.parseLong(req(cli, "pid")); long pid = Long.parseLong(req(cli, "pid"));
Path appJar = Paths.get(req(cli, "app")).toAbsolutePath().normalize(); Path appJar = Path.of(req(cli, "app")).toAbsolutePath().normalize();
String javaBin = cli.getOrDefault("java", "java"); String javaBin = cli.getOrDefault("java", "java");
Path argsFile = cli.containsKey("argsFile") ? Paths.get(cli.get("argsFile")) : null; Path argsFile = cli.containsKey("argsFile") ? Path.of(cli.get("argsFile")) : null;
long backoffMs = Long.parseLong(cli.getOrDefault("backoffMs", "1000")); long backoffMs = Long.parseLong(cli.getOrDefault("backoffMs", "1000"));
if (!Files.isRegularFile(appJar)) { if (!Files.isRegularFile(appJar)) {