diff --git a/Dockerfile b/Dockerfile index 054ed412..86d7bdda 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,10 @@ ARG VERSION_TAG ENV DOCKER_ENABLE_SECURITY=false \ VERSION_TAG=$VERSION_TAG \ JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \ - HOME=/home/stirlingpdfuser + HOME=/home/stirlingpdfuser \ + PUID=1000 \ + PGID=1000 \ + UMASK=022 # JDK for app diff --git a/Dockerfile-lite b/Dockerfile-lite index ea01fa90..982858aa 100644 --- a/Dockerfile-lite +++ b/Dockerfile-lite @@ -7,7 +7,10 @@ ARG VERSION_TAG ENV DOCKER_ENABLE_SECURITY=false \ HOME=/home/stirlingpdfuser \ VERSION_TAG=$VERSION_TAG \ - JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" + JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \ + PUID=1000 \ + PGID=1000 \ + UMASK=022 # Copy necessary files COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh diff --git a/Dockerfile-ultra-lite b/Dockerfile-ultra-lite index 636a9d99..3836f477 100644 --- a/Dockerfile-ultra-lite +++ b/Dockerfile-ultra-lite @@ -7,10 +7,10 @@ ARG VERSION_TAG ENV DOCKER_ENABLE_SECURITY=false \ HOME=/home/stirlingpdfuser \ VERSION_TAG=$VERSION_TAG \ - JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" -# PUID=1000 \ -# PGID=1000 \ -# UMASK=022 \ + JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \ + PUID=1000 \ + PGID=1000 \ + UMASK=022 # Copy necessary files COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh diff --git a/scripts/init-without-ocr.sh b/scripts/init-without-ocr.sh index 486dbdf0..2408b83a 100644 --- a/scripts/init-without-ocr.sh +++ b/scripts/init-without-ocr.sh @@ -1,5 +1,15 @@ #!/bin/sh +# Update the user and group IDs as per environment variables +if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then + usermod -o -u "$PUID" stirlingpdfuser +fi + +if [ ! -z "$PGID" ] && [ "$PGID" != "$(id -g stirlingpdfgroup)" ]; then + groupmod -o -g "$PGID" stirlingpdfgroup +fi +umask "$UMASK" + echo "Setting permissions and ownership for necessary directories..." chown -R stirlingpdfuser:stirlingpdfgroup /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles diff --git a/scripts/init.sh b/scripts/init.sh index 241a609d..b5f266b8 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -13,6 +13,17 @@ if [ -d /usr/share/tesseract-ocr/5/tessdata ]; then cp -r /usr/share/tesseract-ocr/5/tessdata/* /usr/share/tessdata || true; fi + +# Update the user and group IDs as per environment variables +if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then + usermod -o -u "$PUID" stirlingpdfuser +fi + +if [ ! -z "$PGID" ] && [ "$PGID" != "$(id -g stirlingpdfgroup)" ]; then + groupmod -o -g "$PGID" stirlingpdfgroup +fi +umask "$UMASK" + echo "Setting permissions and ownership for necessary directories..." chown -R stirlingpdfuser:stirlingpdfgroup /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /usr/share/tessdata /configs /customFiles diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java index 038e087f..2d059b23 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java @@ -60,5 +60,6 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF return user.isPresent() && user.get().getAuthorities().stream() .anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority())); + } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java index da684280..3eee727f 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java @@ -72,112 +72,146 @@ public class ExtractImageScansController { String extension = fileName.substring(fileName.lastIndexOf(".") + 1); List images = new ArrayList<>(); - - // Check if input file is a PDF - if ("pdf".equalsIgnoreCase(extension)) { - // Load PDF document - try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) { - PDFRenderer pdfRenderer = new PDFRenderer(document); - int pageCount = document.getNumberOfPages(); - images = new ArrayList<>(); - - // Create images of all pages - for (int i = 0; i < pageCount; i++) { - // Create temp file to save the image - Path tempFile = Files.createTempFile("image_", ".png"); - - // Render image and save as temp file - BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300); - ImageIO.write(image, "png", tempFile.toFile()); - - // Add temp file path to images list - images.add(tempFile.toString()); + + List tempImageFiles = new ArrayList<>(); + Path tempInputFile = null; + Path tempZipFile = null; + List tempDirs = new ArrayList<>(); + + try { + // Check if input file is a PDF + if ("pdf".equalsIgnoreCase(extension)) { + // Load PDF document + try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) { + PDFRenderer pdfRenderer = new PDFRenderer(document); + int pageCount = document.getNumberOfPages(); + images = new ArrayList<>(); + + // Create images of all pages + for (int i = 0; i < pageCount; i++) { + // Create temp file to save the image + Path tempFile = Files.createTempFile("image_", ".png"); + + // Render image and save as temp file + BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300); + ImageIO.write(image, "png", tempFile.toFile()); + + // Add temp file path to images list + images.add(tempFile.toString()); + tempImageFiles.add(tempFile); + } + } + } else { + tempInputFile = Files.createTempFile("input_", "." + extension); + Files.copy( + form.getFileInput().getInputStream(), + tempInputFile, + StandardCopyOption.REPLACE_EXISTING); + // Add input file path to images list + images.add(tempInputFile.toString()); + } + + List processedImageBytes = new ArrayList<>(); + + // Process each image + for (int i = 0; i < images.size(); i++) { + + Path tempDir = Files.createTempDirectory("openCV_output"); + tempDirs.add(tempDir); + List command = + new ArrayList<>( + Arrays.asList( + "python3", + "./scripts/split_photos.py", + images.get(i), + tempDir.toString(), + "--angle_threshold", + String.valueOf(form.getAngleThreshold()), + "--tolerance", + String.valueOf(form.getTolerance()), + "--min_area", + String.valueOf(form.getMinArea()), + "--min_contour_area", + String.valueOf(form.getMinContourArea()), + "--border_size", + String.valueOf(form.getBorderSize()))); + + // Run CLI command + ProcessExecutorResult returnCode = + ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) + .runCommandWithOutputHandling(command); + + // Read the output photos in temp directory + List tempOutputFiles = Files.list(tempDir).sorted().collect(Collectors.toList()); + for (Path tempOutputFile : tempOutputFiles) { + byte[] imageBytes = Files.readAllBytes(tempOutputFile); + processedImageBytes.add(imageBytes); + } + // Clean up the temporary directory + FileUtils.deleteDirectory(tempDir.toFile()); + } + + // Create zip file if multiple images + if (processedImageBytes.size() > 1) { + String outputZipFilename = fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip"; + tempZipFile = Files.createTempFile("output_", ".zip"); + + try (ZipOutputStream zipOut = + new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) { + // Add processed images to the zip + for (int i = 0; i < processedImageBytes.size(); i++) { + ZipEntry entry = + new ZipEntry( + fileName.replaceFirst("[.][^.]+$", "") + + "_" + + (i + 1) + + ".png"); + zipOut.putNextEntry(entry); + zipOut.write(processedImageBytes.get(i)); + zipOut.closeEntry(); + } + } + + byte[] zipBytes = Files.readAllBytes(tempZipFile); + + // Clean up the temporary zip file + Files.delete(tempZipFile); + + return WebResponseUtils.bytesToWebResponse( + zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM); + } else { + // Return the processed image as a response + byte[] imageBytes = processedImageBytes.get(0); + return WebResponseUtils.bytesToWebResponse( + imageBytes, + fileName.replaceFirst("[.][^.]+$", "") + ".png", + MediaType.IMAGE_PNG); + } + } finally { + // Cleanup logic for all temporary files and directories + tempImageFiles.forEach(path -> { + try { + Files.deleteIfExists(path); + } catch (IOException e) { + logger.error("Failed to delete temporary image file: " + path, e); } - } - } else { - Path tempInputFile = Files.createTempFile("input_", "." + extension); - Files.copy( - form.getFileInput().getInputStream(), - tempInputFile, - StandardCopyOption.REPLACE_EXISTING); - // Add input file path to images list - images.add(tempInputFile.toString()); - } + }); - List processedImageBytes = new ArrayList<>(); - - // Process each image - for (int i = 0; i < images.size(); i++) { - - Path tempDir = Files.createTempDirectory("openCV_output"); - List command = - new ArrayList<>( - Arrays.asList( - "python3", - "./scripts/split_photos.py", - images.get(i), - tempDir.toString(), - "--angle_threshold", - String.valueOf(form.getAngleThreshold()), - "--tolerance", - String.valueOf(form.getTolerance()), - "--min_area", - String.valueOf(form.getMinArea()), - "--min_contour_area", - String.valueOf(form.getMinContourArea()), - "--border_size", - String.valueOf(form.getBorderSize()))); - - // Run CLI command - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) - .runCommandWithOutputHandling(command); - - // Read the output photos in temp directory - List tempOutputFiles = Files.list(tempDir).sorted().collect(Collectors.toList()); - for (Path tempOutputFile : tempOutputFiles) { - byte[] imageBytes = Files.readAllBytes(tempOutputFile); - processedImageBytes.add(imageBytes); - } - // Clean up the temporary directory - FileUtils.deleteDirectory(tempDir.toFile()); - } - - // Create zip file if multiple images - if (processedImageBytes.size() > 1) { - String outputZipFilename = fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip"; - Path tempZipFile = Files.createTempFile("output_", ".zip"); - - try (ZipOutputStream zipOut = - new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) { - // Add processed images to the zip - for (int i = 0; i < processedImageBytes.size(); i++) { - ZipEntry entry = - new ZipEntry( - fileName.replaceFirst("[.][^.]+$", "") - + "_" - + (i + 1) - + ".png"); - zipOut.putNextEntry(entry); - zipOut.write(processedImageBytes.get(i)); - zipOut.closeEntry(); + if (tempZipFile != null && Files.exists(tempZipFile)) { + try { + Files.delete(tempZipFile); + } catch (IOException e) { + logger.error("Failed to delete temporary zip file: " + tempZipFile, e); } } - byte[] zipBytes = Files.readAllBytes(tempZipFile); - - // Clean up the temporary zip file - Files.delete(tempZipFile); - - return WebResponseUtils.bytesToWebResponse( - zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM); - } else { - // Return the processed image as a response - byte[] imageBytes = processedImageBytes.get(0); - return WebResponseUtils.bytesToWebResponse( - imageBytes, - fileName.replaceFirst("[.][^.]+$", "") + ".png", - MediaType.IMAGE_PNG); + tempDirs.forEach(dir -> { + try { + FileUtils.deleteDirectory(dir.toFile()); + } catch (IOException e) { + logger.error("Failed to delete temporary directory: " + dir, e); + } + }); } } }