diff --git a/README.md b/README.md index a1fe522c9..35164983d 100644 --- a/README.md +++ b/README.md @@ -405,7 +405,7 @@ To access your account settings, go to Account Settings in the settings cog menu To add new users, go to the bottom of Account Settings and hit 'Admin Settings'. Here you can add new users. The different roles mentioned within this are for rate limiting. This is a work in progress and will be expanded on more in the future. -For API usage, you must provide a header with `X-API-Key` and the associated API key for that user. +For API usage, you must provide a header with `X-API-KEY` and the associated API key for that user. ## FAQ diff --git a/cucumber/features/steps/step_definitions.py b/cucumber/features/steps/step_definitions.py index 65a49fda0..ae8acd2ad 100644 --- a/cucumber/features/steps/step_definitions.py +++ b/cucumber/features/steps/step_definitions.py @@ -15,6 +15,10 @@ import shutil import re from PIL import Image, ImageDraw +API_HEADERS = { + 'X-API-KEY': '123456789' +} + ######### # GIVEN # ######### @@ -227,7 +231,7 @@ def save_generated_pdf(context, filename): def step_send_get_request(context, endpoint): base_url = "http://localhost:8080" full_url = f"{base_url}{endpoint}" - response = requests.get(full_url) + response = requests.get(full_url, headers=API_HEADERS) context.response = response @when('I send a GET request to "{endpoint}" with parameters') @@ -235,7 +239,7 @@ def step_send_get_request_with_params(context, endpoint): base_url = "http://localhost:8080" params = {row['parameter']: row['value'] for row in context.table} full_url = f"{base_url}{endpoint}" - response = requests.get(full_url, params=params) + response = requests.get(full_url, params=params, headers=API_HEADERS) context.response = response @when('I send the API request to the endpoint "{endpoint}"') @@ -256,7 +260,7 @@ def step_send_api_request(context, endpoint): print(f"form_data {file.name} with {mime_type}") form_data.append((key, (file.name, file, mime_type))) - response = requests.post(url, files=form_data) + response = requests.post(url, files=form_data, headers=API_HEADERS) context.response = response ######## diff --git a/exampleYmlFiles/test_cicd.yml b/exampleYmlFiles/test_cicd.yml new file mode 100644 index 000000000..144348a79 --- /dev/null +++ b/exampleYmlFiles/test_cicd.yml @@ -0,0 +1,34 @@ +services: + stirling-pdf: + container_name: Stirling-PDF-Security-Fat + image: stirlingtools/stirling-pdf:latest-fat + deploy: + resources: + limits: + memory: 4G + healthcheck: + test: ["CMD-SHELL", "curl -f -H 'X-API-KEY: 123456789' http://localhost:8080/api/v1/info/status | grep -q 'UP'"] + interval: 5s + timeout: 10s + retries: 16 + ports: + - 8080:8080 + volumes: + - /stirling/latest/data:/usr/share/tessdata:rw + - /stirling/latest/config:/configs:rw + - /stirling/latest/logs:/logs:rw + environment: + DOCKER_ENABLE_SECURITY: "true" + SECURITY_ENABLELOGIN: "true" + PUID: 1002 + PGID: 1002 + UMASK: "022" + SYSTEM_DEFAULTLOCALE: en-US + UI_APPNAME: Stirling-PDF + UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest-fat with Security + UI_APPNAMENAVBAR: Stirling-PDF Latest-fat + SYSTEM_MAXFILESIZE: "100" + METRICS_ENABLED: "true" + SYSTEM_GOOGLEVISIBILITY: "true" + SECURITY_CUSTOMGLOBALAPIKEY: "123456789" + restart: on-failure:5 diff --git a/src/main/java/stirling/software/SPDF/EE/LicenseKeyChecker.java b/src/main/java/stirling/software/SPDF/EE/LicenseKeyChecker.java index 93648dfa2..108ee3020 100644 --- a/src/main/java/stirling/software/SPDF/EE/LicenseKeyChecker.java +++ b/src/main/java/stirling/software/SPDF/EE/LicenseKeyChecker.java @@ -28,7 +28,7 @@ public class LicenseKeyChecker { this.checkLicense(); } - @Scheduled(initialDelay = 604800000,fixedRate = 604800000) // 7 days in milliseconds + @Scheduled(initialDelay = 604800000, fixedRate = 604800000) // 7 days in milliseconds public void checkLicensePeriodically() { checkLicense(); } diff --git a/src/main/java/stirling/software/SPDF/config/InitialSetup.java b/src/main/java/stirling/software/SPDF/config/InitialSetup.java index 0e0ad2be0..81279ce9f 100644 --- a/src/main/java/stirling/software/SPDF/config/InitialSetup.java +++ b/src/main/java/stirling/software/SPDF/config/InitialSetup.java @@ -1,11 +1,14 @@ package stirling.software.SPDF.config; import java.io.IOException; +import java.util.Properties; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import io.micrometer.common.util.StringUtils; @@ -23,6 +26,18 @@ public class InitialSetup { @Autowired private ApplicationProperties applicationProperties; @PostConstruct + public void init() throws IOException { + initUUIDKey(); + + initSecretKey(); + + initEnableCSRFSecurity(); + + initLegalUrls(); + + initSetAppVersion(); + } + public void initUUIDKey() throws IOException { String uuid = applicationProperties.getAutomaticallyGenerated().getUUID(); if (!GeneralUtils.isValidUUID(uuid)) { @@ -32,7 +47,6 @@ public class InitialSetup { } } - @PostConstruct public void initSecretKey() throws IOException { String secretKey = applicationProperties.getAutomaticallyGenerated().getKey(); if (!GeneralUtils.isValidUUID(secretKey)) { @@ -42,13 +56,24 @@ public class InitialSetup { } } - @PostConstruct + public void initEnableCSRFSecurity() throws IOException { + if (GeneralUtils.isVersionHigher( + "0.36.0", applicationProperties.getAutomaticallyGenerated().getAppVersion())) { + Boolean csrf = applicationProperties.getSecurity().getCsrfDisabled(); + if (!csrf) { + GeneralUtils.saveKeyToConfig("security.csrfDisabled", false, false); + GeneralUtils.saveKeyToConfig("system.enableAnalytics", "true", false); + applicationProperties.getSecurity().setCsrfDisabled(false); + } + } + } + public void initLegalUrls() throws IOException { // Initialize Terms and Conditions String termsUrl = applicationProperties.getLegal().getTermsAndConditions(); if (StringUtils.isEmpty(termsUrl)) { String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions"; - GeneralUtils.saveKeyToConfig("legal.termsAndConditions", defaultTermsUrl); + GeneralUtils.saveKeyToConfig("legal.termsAndConditions", defaultTermsUrl, false); applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl); } @@ -56,8 +81,23 @@ public class InitialSetup { String privacyUrl = applicationProperties.getLegal().getPrivacyPolicy(); if (StringUtils.isEmpty(privacyUrl)) { String defaultPrivacyUrl = "https://www.stirlingpdf.com/privacy-policy"; - GeneralUtils.saveKeyToConfig("legal.privacyPolicy", defaultPrivacyUrl); + GeneralUtils.saveKeyToConfig("legal.privacyPolicy", defaultPrivacyUrl, false); applicationProperties.getLegal().setPrivacyPolicy(defaultPrivacyUrl); } } + + public void initSetAppVersion() throws IOException { + + String appVersion = "0.0.0"; + Resource resource = new ClassPathResource("version.properties"); + Properties props = new Properties(); + try { + props.load(resource.getInputStream()); + appVersion = props.getProperty("version"); + } catch (Exception e) { + + } + applicationProperties.getAutomaticallyGenerated().setAppVersion(appVersion); + GeneralUtils.saveKeyToConfig("AutomaticallyGenerated.appVersion", appVersion, false); + } } diff --git a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java index 7e542a003..2fcebcad8 100644 --- a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java +++ b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java @@ -75,5 +75,7 @@ public class InitialSecuritySetup { userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId()); log.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId()); } + userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey()); + System.out.println(applicationProperties.getSecurity().getCustomGlobalAPIKey()); } } diff --git a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java index fa3f5342e..66e6f0f5f 100644 --- a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java @@ -99,7 +99,7 @@ public class SecurityConfiguration { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - if (applicationProperties.getSecurity().getCsrfDisabled()) { + if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) { http.csrf(csrf -> csrf.disable()); } @@ -116,7 +116,7 @@ public class SecurityConfiguration { csrf -> csrf.ignoringRequestMatchers( request -> { - String apiKey = request.getHeader("X-API-Key"); + String apiKey = request.getHeader("X-API-KEY"); // If there's no API key, don't ignore CSRF // (return false) @@ -289,17 +289,17 @@ public class SecurityConfiguration { } } else { - if (!applicationProperties.getSecurity().getCsrfDisabled()) { - CookieCsrfTokenRepository cookieRepo = - CookieCsrfTokenRepository.withHttpOnlyFalse(); - CsrfTokenRequestAttributeHandler requestHandler = - new CsrfTokenRequestAttributeHandler(); - requestHandler.setCsrfRequestAttributeName(null); - http.csrf( - csrf -> - csrf.csrfTokenRepository(cookieRepo) - .csrfTokenRequestHandler(requestHandler)); - } + // if (!applicationProperties.getSecurity().getCsrfDisabled()) { + // CookieCsrfTokenRepository cookieRepo = + // CookieCsrfTokenRepository.withHttpOnlyFalse(); + // CsrfTokenRequestAttributeHandler requestHandler = + // new CsrfTokenRequestAttributeHandler(); + // requestHandler.setCsrfRequestAttributeName(null); + // http.csrf( + // csrf -> + // csrf.csrfTokenRepository(cookieRepo) + // .csrfTokenRequestHandler(requestHandler)); + // } http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll()); } diff --git a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java index 4b62f6d29..93dff07bb 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java @@ -71,7 +71,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { // Check for API key in the request headers if no authentication exists if (authentication == null || !authentication.isAuthenticated()) { - String apiKey = request.getHeader("X-API-Key"); + String apiKey = request.getHeader("X-API-KEY"); if (apiKey != null && !apiKey.trim().isEmpty()) { try { // Use API key to authenticate. This requires you to have an authentication diff --git a/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java b/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java index bd7e39725..b17334941 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java @@ -59,7 +59,7 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter { String identifier = null; // Check for API key in the request headers - String apiKey = request.getHeader("X-API-Key"); + String apiKey = request.getHeader("X-API-KEY"); if (apiKey != null && !apiKey.trim().isEmpty()) { identifier = "API_KEY_" + apiKey; // Prefix to distinguish between API keys and usernames @@ -79,7 +79,7 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter { Role userRole = getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication()); - if (request.getHeader("X-API-Key") != null) { + if (request.getHeader("X-API-KEY") != null) { // It's an API call processRequest( userRole.getApiCallsPerDay(), diff --git a/src/main/java/stirling/software/SPDF/config/security/UserService.java b/src/main/java/stirling/software/SPDF/config/security/UserService.java index d7f35d387..d5fea5942 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserService.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserService.java @@ -390,6 +390,37 @@ public class UserService implements UserServiceInterface { } } + @Transactional + public void syncCustomApiUser(String customApiKey) throws IOException { + if (customApiKey == null || customApiKey.trim().length() == 0) { + return; + } + String username = "CUSTOM_API_USER"; + Optional existingUser = findByUsernameIgnoreCase(username); + + if (!existingUser.isPresent()) { + // Create new user with API role + User user = new User(); + user.setUsername(username); + user.setPassword(UUID.randomUUID().toString()); + user.setEnabled(true); + user.setFirstLogin(false); + user.setAuthenticationType(AuthenticationType.WEB); + user.setApiKey(customApiKey); + user.addAuthority(new Authority(Role.INTERNAL_API_USER.getRoleId(), user)); + userRepository.save(user); + databaseBackupHelper.exportDatabase(); + } else { + // Update API key if it has changed + User user = existingUser.get(); + if (!customApiKey.equals(user.getApiKey())) { + user.setApiKey(customApiKey); + userRepository.save(user); + databaseBackupHelper.exportDatabase(); + } + } + } + @Override public long getTotalUsersCount() { return userRepository.count(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java index e27df1033..a8e74e2a9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java @@ -52,84 +52,115 @@ public class SplitPDFController { "This endpoint splits a given PDF file into separate documents based on the specified page numbers or ranges. Users can specify pages using individual numbers, ranges, or 'all' for every page. Input:PDF Output:PDF Type:SIMO") public ResponseEntity splitPdf(@ModelAttribute PDFWithPageNums request) throws IOException { - MultipartFile file = request.getFileInput(); - String pages = request.getPageNumbers(); - // open the pdf document - PDDocument document = Loader.loadPDF(file.getBytes()); - // PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document); - int totalPages = document.getNumberOfPages(); - List pageNumbers = request.getPageNumbersList(document, false); - if (!pageNumbers.contains(totalPages - 1)) { - // Create a mutable ArrayList so we can add to it - pageNumbers = new ArrayList<>(pageNumbers); - pageNumbers.add(totalPages - 1); - } - - logger.info( - "Splitting PDF into pages: {}", - pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(","))); - - // split the document + PDDocument document = null; + Path zipFile = null; List splitDocumentsBoas = new ArrayList<>(); - int previousPageNumber = 0; - for (int splitPoint : pageNumbers) { - try (PDDocument splitDocument = - pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) { - for (int i = previousPageNumber; i <= splitPoint; i++) { - PDPage page = document.getPage(i); - splitDocument.addPage(page); - logger.info("Adding page {} to split document", i); + + try { + + MultipartFile file = request.getFileInput(); + String pages = request.getPageNumbers(); + // open the pdf document + + document = Loader.loadPDF(file.getBytes()); + // PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document); + int totalPages = document.getNumberOfPages(); + List pageNumbers = request.getPageNumbersList(document, false); + if (!pageNumbers.contains(totalPages - 1)) { + // Create a mutable ArrayList so we can add to it + pageNumbers = new ArrayList<>(pageNumbers); + pageNumbers.add(totalPages - 1); + } + + logger.info( + "Splitting PDF into pages: {}", + pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(","))); + + // split the document + splitDocumentsBoas = new ArrayList<>(); + int previousPageNumber = 0; + for (int splitPoint : pageNumbers) { + try (PDDocument splitDocument = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument(document)) { + for (int i = previousPageNumber; i <= splitPoint; i++) { + PDPage page = document.getPage(i); + splitDocument.addPage(page); + logger.info("Adding page {} to split document", i); + } + previousPageNumber = splitPoint + 1; + + // Transfer metadata to split pdf + // PdfMetadataService.setMetadataToPdf(splitDocument, metadata); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + splitDocument.save(baos); + + splitDocumentsBoas.add(baos); + } catch (Exception e) { + logger.error("Failed splitting documents and saving them", e); + throw e; } - previousPageNumber = splitPoint + 1; + } - // Transfer metadata to split pdf - // PdfMetadataService.setMetadataToPdf(splitDocument, metadata); + // closing the original document + document.close(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - splitDocument.save(baos); + zipFile = Files.createTempFile("split_documents", ".zip"); - splitDocumentsBoas.add(baos); + String filename = + Filenames.toSimpleFileName(file.getOriginalFilename()) + .replaceFirst("[.][^.]+$", ""); + try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { + // loop through the split documents and write them to the zip file + for (int i = 0; i < splitDocumentsBoas.size(); i++) { + String fileName = filename + "_" + (i + 1) + ".pdf"; + ByteArrayOutputStream baos = splitDocumentsBoas.get(i); + byte[] pdf = baos.toByteArray(); + + // Add PDF file to the zip + ZipEntry pdfEntry = new ZipEntry(fileName); + zipOut.putNextEntry(pdfEntry); + zipOut.write(pdf); + zipOut.closeEntry(); + + logger.info("Wrote split document {} to zip file", fileName); + } } catch (Exception e) { - logger.error("Failed splitting documents and saving them", e); + logger.error("Failed writing to zip", e); throw e; } - } - // closing the original document - document.close(); + logger.info( + "Successfully created zip file with split documents: {}", zipFile.toString()); + byte[] data = Files.readAllBytes(zipFile); + Files.deleteIfExists(zipFile); - Path zipFile = Files.createTempFile("split_documents", ".zip"); + // return the Resource in the response + return WebResponseUtils.bytesToWebResponse( + data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); - String filename = - Filenames.toSimpleFileName(file.getOriginalFilename()) - .replaceFirst("[.][^.]+$", ""); - try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { - // loop through the split documents and write them to the zip file - for (int i = 0; i < splitDocumentsBoas.size(); i++) { - String fileName = filename + "_" + (i + 1) + ".pdf"; - ByteArrayOutputStream baos = splitDocumentsBoas.get(i); - byte[] pdf = baos.toByteArray(); + } finally { + try { + // Close the main document + if (document != null) { + document.close(); + } - // Add PDF file to the zip - ZipEntry pdfEntry = new ZipEntry(fileName); - zipOut.putNextEntry(pdfEntry); - zipOut.write(pdf); - zipOut.closeEntry(); + // Close all ByteArrayOutputStreams + for (ByteArrayOutputStream baos : splitDocumentsBoas) { + if (baos != null) { + baos.close(); + } + } - logger.info("Wrote split document {} to zip file", fileName); + // Delete temporary zip file + if (zipFile != null) { + Files.deleteIfExists(zipFile); + } + } catch (Exception e) { + logger.error("Error while cleaning up resources", e); } - } catch (Exception e) { - logger.error("Failed writing to zip", e); - throw e; } - - logger.info("Successfully created zip file with split documents: {}", zipFile.toString()); - byte[] data = Files.readAllBytes(zipFile); - Files.deleteIfExists(zipFile); - - // return the Resource in the response - return WebResponseUtils.bytesToWebResponse( - data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java index f344c2763..c74ed2940 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java @@ -59,70 +59,86 @@ public class SplitPdfByChaptersController { public ResponseEntity splitPdf(@ModelAttribute SplitPdfByChaptersRequest request) throws Exception { MultipartFile file = request.getFileInput(); - boolean includeMetadata = request.getIncludeMetadata(); - Integer bookmarkLevel = - request.getBookmarkLevel(); // levels start from 0 (top most bookmarks) - if (bookmarkLevel < 0) { - return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes()); - } - PDDocument sourceDocument = Loader.loadPDF(file.getBytes()); + PDDocument sourceDocument = null; + Path zipFile = null; - PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline(); - - if (outline == null) { - logger.warn("No outline found for {}", file.getOriginalFilename()); - return ResponseEntity.badRequest().body("No outline found".getBytes()); - } - List bookmarks = new ArrayList<>(); try { - bookmarks = - extractOutlineItems( - sourceDocument, - outline.getFirstChild(), - bookmarks, - outline.getFirstChild().getNextSibling(), - 0, - bookmarkLevel); - // to handle last page edge case - bookmarks.get(bookmarks.size() - 1).setEndPage(sourceDocument.getNumberOfPages()); - Bookmark lastBookmark = bookmarks.get(bookmarks.size() - 1); + boolean includeMetadata = request.getIncludeMetadata(); + Integer bookmarkLevel = + request.getBookmarkLevel(); // levels start from 0 (top most bookmarks) + if (bookmarkLevel < 0) { + return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes()); + } + sourceDocument = Loader.loadPDF(file.getBytes()); - } catch (Exception e) { - logger.error("Unable to extract outline items", e); - return ResponseEntity.internalServerError() - .body("Unable to extract outline items".getBytes()); + PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline(); + + if (outline == null) { + logger.warn("No outline found for {}", file.getOriginalFilename()); + return ResponseEntity.badRequest().body("No outline found".getBytes()); + } + List bookmarks = new ArrayList<>(); + try { + bookmarks = + extractOutlineItems( + sourceDocument, + outline.getFirstChild(), + bookmarks, + outline.getFirstChild().getNextSibling(), + 0, + bookmarkLevel); + // to handle last page edge case + bookmarks.get(bookmarks.size() - 1).setEndPage(sourceDocument.getNumberOfPages()); + Bookmark lastBookmark = bookmarks.get(bookmarks.size() - 1); + + } catch (Exception e) { + logger.error("Unable to extract outline items", e); + return ResponseEntity.internalServerError() + .body("Unable to extract outline items".getBytes()); + } + + boolean allowDuplicates = request.getAllowDuplicates(); + if (!allowDuplicates) { + /* + duplicates are generated when multiple bookmarks correspond to the same page, + if the user doesn't want duplicates mergeBookmarksThatCorrespondToSamePage() method will merge the titles of all + the bookmarks that correspond to the same page, and treat them as a single bookmark + */ + bookmarks = mergeBookmarksThatCorrespondToSamePage(bookmarks); + } + for (Bookmark bookmark : bookmarks) { + logger.info( + "{}::::{} to {}", + bookmark.getTitle(), + bookmark.getStartPage(), + bookmark.getEndPage()); + } + List splitDocumentsBoas = + getSplitDocumentsBoas(sourceDocument, bookmarks, includeMetadata); + + zipFile = createZipFile(bookmarks, splitDocumentsBoas); + + byte[] data = Files.readAllBytes(zipFile); + Files.deleteIfExists(zipFile); + + String filename = + Filenames.toSimpleFileName(file.getOriginalFilename()) + .replaceFirst("[.][^.]+$", ""); + sourceDocument.close(); + return WebResponseUtils.bytesToWebResponse( + data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); + } finally { + try { + if (sourceDocument != null) { + sourceDocument.close(); + } + if (zipFile != null) { + Files.deleteIfExists(zipFile); + } + } catch (Exception e) { + logger.error("Error while cleaning up resources", e); + } } - - boolean allowDuplicates = request.getAllowDuplicates(); - if (!allowDuplicates) { - /* - duplicates are generated when multiple bookmarks correspond to the same page, - if the user doesn't want duplicates mergeBookmarksThatCorrespondToSamePage() method will merge the titles of all - the bookmarks that correspond to the same page, and treat them as a single bookmark - */ - bookmarks = mergeBookmarksThatCorrespondToSamePage(bookmarks); - } - for (Bookmark bookmark : bookmarks) { - logger.info( - "{}::::{} to {}", - bookmark.getTitle(), - bookmark.getStartPage(), - bookmark.getEndPage()); - } - List splitDocumentsBoas = - getSplitDocumentsBoas(sourceDocument, bookmarks, includeMetadata); - - Path zipFile = createZipFile(bookmarks, splitDocumentsBoas); - - byte[] data = Files.readAllBytes(zipFile); - Files.deleteIfExists(zipFile); - - String filename = - Filenames.toSimpleFileName(file.getOriginalFilename()) - .replaceFirst("[.][^.]+$", ""); - sourceDocument.close(); - return WebResponseUtils.bytesToWebResponse( - data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); } private List mergeBookmarksThatCorrespondToSamePage(List bookmarks) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java index 2b4f13133..eaa9c86d9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java @@ -105,15 +105,13 @@ public class SplitPdfBySectionsController { if (sectionNum == horiz * verti) pageNum++; } - } catch (Exception e) { - logger.error("exception", e); - } finally { data = Files.readAllBytes(zipFile); + return WebResponseUtils.bytesToWebResponse( + data, filename + "_split.zip", MediaType.APPLICATION_OCTET_STREAM); + + } finally { Files.deleteIfExists(zipFile); } - - return WebResponseUtils.bytesToWebResponse( - data, filename + "_split.zip", MediaType.APPLICATION_OCTET_STREAM); } public List splitPdfPages( diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java index be955dbd3..b5eec3923 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java @@ -65,112 +65,137 @@ public class ConvertImgPDFController { String colorType = request.getColorType(); String dpi = request.getDpi(); - byte[] pdfBytes = file.getBytes(); - ImageType colorTypeResult = ImageType.RGB; - if ("greyscale".equals(colorType)) { - colorTypeResult = ImageType.GRAY; - } else if ("blackwhite".equals(colorType)) { - colorTypeResult = ImageType.BINARY; - } - // returns bytes for image - boolean singleImage = "single".equals(singleOrMultiple); + Path tempFile = null; + Path tempOutputDir = null; + Path tempPdfPath = null; byte[] result = null; - String filename = - Filenames.toSimpleFileName(file.getOriginalFilename()) - .replaceFirst("[.][^.]+$", ""); - result = - PdfUtils.convertFromPdf( - pdfBytes, - "webp".equalsIgnoreCase(imageFormat) ? "png" : imageFormat.toUpperCase(), - colorTypeResult, - singleImage, - Integer.valueOf(dpi), - filename); - if (result == null || result.length == 0) { - logger.error("resultant bytes for {} is null, error converting ", filename); - } - if ("webp".equalsIgnoreCase(imageFormat) && !CheckProgramInstall.isPythonAvailable()) { - throw new IOException("Python is not installed. Required for WebP conversion."); - } else if ("webp".equalsIgnoreCase(imageFormat) - && CheckProgramInstall.isPythonAvailable()) { - // Write the output stream to a temp file - Path tempFile = Files.createTempFile("temp_png", ".png"); - try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) { - fos.write(result); - fos.flush(); + try { + byte[] pdfBytes = file.getBytes(); + ImageType colorTypeResult = ImageType.RGB; + if ("greyscale".equals(colorType)) { + colorTypeResult = ImageType.GRAY; + } else if ("blackwhite".equals(colorType)) { + colorTypeResult = ImageType.BINARY; } + // returns bytes for image + boolean singleImage = "single".equals(singleOrMultiple); + String filename = + Filenames.toSimpleFileName(file.getOriginalFilename()) + .replaceFirst("[.][^.]+$", ""); - String pythonVersion = CheckProgramInstall.getAvailablePythonCommand(); - - List command = new ArrayList<>(); - command.add(pythonVersion); - command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion - - // Create a temporary directory for the output WebP files - Path tempOutputDir = Files.createTempDirectory("webp_output"); - if (singleImage) { - // Run the Python script to convert PNG to WebP - command.add(tempFile.toString()); - command.add(tempOutputDir.toString()); - command.add("--single"); - } else { - // Save the uploaded PDF to a temporary file - Path tempPdfPath = Files.createTempFile("temp_pdf", ".pdf"); - file.transferTo(tempPdfPath.toFile()); - // Run the Python script to convert PDF to WebP - command.add(tempPdfPath.toString()); - command.add(tempOutputDir.toString()); + result = + PdfUtils.convertFromPdf( + pdfBytes, + "webp".equalsIgnoreCase(imageFormat) + ? "png" + : imageFormat.toUpperCase(), + colorTypeResult, + singleImage, + Integer.valueOf(dpi), + filename); + if (result == null || result.length == 0) { + logger.error("resultant bytes for {} is null, error converting ", filename); } - command.add("--dpi"); - command.add(dpi); - ProcessExecutorResult resultProcess = - ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) - .runCommandWithOutputHandling(command); - - // Find all WebP files in the output directory - List webpFiles = - Files.walk(tempOutputDir) - .filter(path -> path.toString().endsWith(".webp")) - .collect(Collectors.toList()); - - if (webpFiles.isEmpty()) { - logger.error("No WebP files were created in: {}", tempOutputDir.toString()); - throw new IOException("No WebP files were created. " + resultProcess.getMessages()); - } - - byte[] bodyBytes = new byte[0]; - - if (webpFiles.size() == 1) { - // Return the single WebP file directly - Path webpFilePath = webpFiles.get(0); - bodyBytes = Files.readAllBytes(webpFilePath); - } else { - // Create a ZIP file containing all WebP images - ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream(); - try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) { - for (Path webpFile : webpFiles) { - zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString())); - Files.copy(webpFile, zos); - zos.closeEntry(); - } + if ("webp".equalsIgnoreCase(imageFormat) && !CheckProgramInstall.isPythonAvailable()) { + throw new IOException("Python is not installed. Required for WebP conversion."); + } else if ("webp".equalsIgnoreCase(imageFormat) + && CheckProgramInstall.isPythonAvailable()) { + // Write the output stream to a temp file + tempFile = Files.createTempFile("temp_png", ".png"); + try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) { + fos.write(result); + fos.flush(); } - bodyBytes = zipOutputStream.toByteArray(); - } - // Clean up the temporary files - Files.deleteIfExists(tempFile); - if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile()); - result = bodyBytes; - } - if (singleImage) { - String docName = filename + "." + imageFormat; - MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat)); - return WebResponseUtils.bytesToWebResponse(result, docName, mediaType); - } else { - String zipFilename = filename + "_convertedToImages.zip"; - return WebResponseUtils.bytesToWebResponse( - result, zipFilename, MediaType.APPLICATION_OCTET_STREAM); + String pythonVersion = CheckProgramInstall.getAvailablePythonCommand(); + + List command = new ArrayList<>(); + command.add(pythonVersion); + command.add("./scripts/png_to_webp.py"); // Python script to handle the conversion + + // Create a temporary directory for the output WebP files + tempOutputDir = Files.createTempDirectory("webp_output"); + if (singleImage) { + // Run the Python script to convert PNG to WebP + command.add(tempFile.toString()); + command.add(tempOutputDir.toString()); + command.add("--single"); + } else { + // Save the uploaded PDF to a temporary file + tempPdfPath = Files.createTempFile("temp_pdf", ".pdf"); + file.transferTo(tempPdfPath.toFile()); + // Run the Python script to convert PDF to WebP + command.add(tempPdfPath.toString()); + command.add(tempOutputDir.toString()); + } + command.add("--dpi"); + command.add(dpi); + ProcessExecutorResult resultProcess = + ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) + .runCommandWithOutputHandling(command); + + // Find all WebP files in the output directory + List webpFiles = + Files.walk(tempOutputDir) + .filter(path -> path.toString().endsWith(".webp")) + .collect(Collectors.toList()); + + if (webpFiles.isEmpty()) { + logger.error("No WebP files were created in: {}", tempOutputDir.toString()); + throw new IOException( + "No WebP files were created. " + resultProcess.getMessages()); + } + + byte[] bodyBytes = new byte[0]; + + if (webpFiles.size() == 1) { + // Return the single WebP file directly + Path webpFilePath = webpFiles.get(0); + bodyBytes = Files.readAllBytes(webpFilePath); + } else { + // Create a ZIP file containing all WebP images + ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(zipOutputStream)) { + for (Path webpFile : webpFiles) { + zos.putNextEntry(new ZipEntry(webpFile.getFileName().toString())); + Files.copy(webpFile, zos); + zos.closeEntry(); + } + } + bodyBytes = zipOutputStream.toByteArray(); + } + // Clean up the temporary files + Files.deleteIfExists(tempFile); + if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile()); + result = bodyBytes; + } + + if (singleImage) { + String docName = filename + "." + imageFormat; + MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat)); + return WebResponseUtils.bytesToWebResponse(result, docName, mediaType); + } else { + String zipFilename = filename + "_convertedToImages.zip"; + return WebResponseUtils.bytesToWebResponse( + result, zipFilename, MediaType.APPLICATION_OCTET_STREAM); + } + + } finally { + try { + // Clean up temporary files + if (tempFile != null) { + Files.deleteIfExists(tempFile); + } + if (tempPdfPath != null) { + Files.deleteIfExists(tempPdfPath); + } + if (tempOutputDir != null) { + FileUtils.deleteDirectory(tempOutputDir.toFile()); + } + } catch (Exception e) { + logger.error("Error cleaning up temporary files", e); + } } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java index 6c5f3993a..f503c107a 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java @@ -87,7 +87,7 @@ public class OCRController { Files.createDirectories(tempOutputDir); Files.createDirectories(tempImagesDir); - + Process process = null; try { // Save input file inputFile.transferTo(tempInputFile.toFile()); @@ -139,7 +139,7 @@ public class OCRController { command.add("pdf"); // Always output PDF ProcessBuilder pb = new ProcessBuilder(command); - Process process = pb.start(); + process = pb.start(); // Capture any error output try (BufferedReader reader = @@ -188,6 +188,10 @@ public class OCRController { .body(pdfContent); } finally { + if (process != null) { + process.destroy(); + } + // Clean up temporary files deleteDirectory(tempDir); } diff --git a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java index 2e62c3505..004960369 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java +++ b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java @@ -221,7 +221,7 @@ public class PipelineProcessor { HttpHeaders headers = new HttpHeaders(); String apiKey = getApiKeyForUser(); - headers.add("X-API-Key", apiKey); + headers.add("X-API-KEY", apiKey); headers.setContentType(MediaType.MULTIPART_FORM_DATA); // Create HttpEntity with the body and headers diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java b/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java index 96745c4a5..d6950e955 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java @@ -595,7 +595,9 @@ public class GetInfoOnPDF { permissionsNode.put("Document Assembly", getPermissionState(ap.canAssembleDocument())); permissionsNode.put("Extracting Content", getPermissionState(ap.canExtractContent())); - permissionsNode.put("Extracting for accessibility", getPermissionState(ap.canExtractForAccessibility())); + permissionsNode.put( + "Extracting for accessibility", + getPermissionState(ap.canExtractForAccessibility())); permissionsNode.put("Form Filling", getPermissionState(ap.canFillInForm())); permissionsNode.put("Modifying", getPermissionState(ap.canModify())); permissionsNode.put("Modifying annotations", getPermissionState(ap.canModifyAnnotations())); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java b/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java index 94e99dd93..317c6424b 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/ValidateSignatureController.java @@ -92,20 +92,29 @@ public class ValidateSignatureController { SignerInformationStore signerStore = signedData.getSignerInfos(); for (SignerInformation signer : signerStore.getSigners()) { - X509CertificateHolder certHolder = (X509CertificateHolder) certStore.getMatches(signer.getSID()).iterator().next(); - X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certHolder); + X509CertificateHolder certHolder = + (X509CertificateHolder) + certStore.getMatches(signer.getSID()).iterator().next(); + X509Certificate cert = + new JcaX509CertificateConverter().getCertificate(certHolder); - boolean isValid = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert)); + boolean isValid = + signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert)); result.setValid(isValid); // Additional validations - result.setChainValid(customCert != null - ? certValidationService.validateCertificateChainWithCustomCert(cert, customCert) - : certValidationService.validateCertificateChain(cert)); + result.setChainValid( + customCert != null + ? certValidationService + .validateCertificateChainWithCustomCert( + cert, customCert) + : certValidationService.validateCertificateChain(cert)); - result.setTrustValid(customCert != null - ? certValidationService.validateTrustWithCustomCert(cert, customCert) - : certValidationService.validateTrustStore(cert)); + result.setTrustValid( + customCert != null + ? certValidationService.validateTrustWithCustomCert( + cert, customCert) + : certValidationService.validateTrustStore(cert)); result.setNotRevoked(!certValidationService.isRevoked(cert)); result.setNotExpired(!cert.getNotAfter().before(new Date())); @@ -123,17 +132,18 @@ public class ValidateSignatureController { result.setValidFrom(cert.getNotBefore().toString()); result.setValidUntil(cert.getNotAfter().toString()); result.setSignatureAlgorithm(cert.getSigAlgName()); - + // Get key size (if possible) try { - result.setKeySize(((RSAPublicKey) cert.getPublicKey()).getModulus().bitLength()); + result.setKeySize( + ((RSAPublicKey) cert.getPublicKey()).getModulus().bitLength()); } catch (Exception e) { // If not RSA or error, set to 0 result.setKeySize(0); } result.setVersion(String.valueOf(cert.getVersion())); - + // Set key usage List keyUsages = new ArrayList<>(); boolean[] keyUsageFlags = cert.getKeyUsage(); @@ -150,9 +160,11 @@ public class ValidateSignatureController { } } result.setKeyUsages(keyUsages); - + // Check if self-signed - result.setSelfSigned(cert.getSubjectX500Principal().equals(cert.getIssuerX500Principal())); + result.setSelfSigned( + cert.getSubjectX500Principal() + .equals(cert.getIssuerX500Principal())); } } catch (Exception e) { result.setValid(false); diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java index 6d4d0a7f4..703b5ee6f 100644 --- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java +++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java @@ -73,6 +73,7 @@ public class ApplicationProperties { private int loginAttemptCount; private long loginResetTimeMinutes; private String loginMethod = "all"; + private String customGlobalAPIKey; public Boolean isAltLogin() { return saml2.getEnabled() || oauth2.getEnabled(); @@ -285,6 +286,7 @@ public class ApplicationProperties { public static class AutomaticallyGenerated { @ToString.Exclude private String key; private String UUID; + private String appVersion; } @Data diff --git a/src/main/java/stirling/software/SPDF/model/api/security/SignatureValidationResult.java b/src/main/java/stirling/software/SPDF/model/api/security/SignatureValidationResult.java index 1aafd8eca..b4c51f365 100644 --- a/src/main/java/stirling/software/SPDF/model/api/security/SignatureValidationResult.java +++ b/src/main/java/stirling/software/SPDF/model/api/security/SignatureValidationResult.java @@ -16,16 +16,15 @@ public class SignatureValidationResult { private boolean trustValid; private boolean notExpired; private boolean notRevoked; - - private String issuerDN; // Certificate issuer's Distinguished Name - private String subjectDN; // Certificate subject's Distinguished Name - private String serialNumber; // Certificate serial number - private String validFrom; // Certificate validity start date - private String validUntil; // Certificate validity end date - private String signatureAlgorithm;// Algorithm used for signing - private int keySize; // Key size in bits - private String version; // Certificate version - private List keyUsages; // List of key usage purposes - private boolean isSelfSigned; // Whether the certificate is self-signed - + + private String issuerDN; // Certificate issuer's Distinguished Name + private String subjectDN; // Certificate subject's Distinguished Name + private String serialNumber; // Certificate serial number + private String validFrom; // Certificate validity start date + private String validUntil; // Certificate validity end date + private String signatureAlgorithm; // Algorithm used for signing + private int keySize; // Key size in bits + private String version; // Certificate version + private List keyUsages; // List of key usage purposes + private boolean isSelfSigned; // Whether the certificate is self-signed } diff --git a/src/main/java/stirling/software/SPDF/service/CertificateValidationService.java b/src/main/java/stirling/software/SPDF/service/CertificateValidationService.java index 41f54f4aa..550db6801 100644 --- a/src/main/java/stirling/software/SPDF/service/CertificateValidationService.java +++ b/src/main/java/stirling/software/SPDF/service/CertificateValidationService.java @@ -1,6 +1,5 @@ package stirling.software.SPDF.service; -import io.github.pixee.security.BoundedLineReader; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -24,6 +23,8 @@ import java.util.Set; import org.springframework.stereotype.Service; +import io.github.pixee.security.BoundedLineReader; + import jakarta.annotation.PostConstruct; @Service diff --git a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java index 8e56c8df6..b5654c7dc 100644 --- a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java @@ -289,6 +289,10 @@ public class GeneralUtils { saveKeyToConfig(id, key, true); } + public static void saveKeyToConfig(String id, boolean key) throws IOException { + saveKeyToConfig(id, key, true); + } + public static void saveKeyToConfig(String id, String key, boolean autoGenerated) throws IOException { Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml @@ -307,6 +311,24 @@ public class GeneralUtils { settingsYml.save(); } + public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated) + throws IOException { + Path path = Paths.get("configs", "settings.yml"); + + final YamlFile settingsYml = new YamlFile(path.toFile()); + DumperOptions yamlOptionssettingsYml = + ((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions(); + yamlOptionssettingsYml.setSplitLines(false); + + settingsYml.loadWithComments(); + + YamlFileWrapper writer = settingsYml.path(id).set(key); + if (autoGenerated) { + writer.comment("# Automatically Generated Settings (Do Not Edit Directly)"); + } + settingsYml.save(); + } + public static String generateMachineFingerprint() { try { // Get the MAC address @@ -349,4 +371,33 @@ public class GeneralUtils { return "GenericID"; } } + + public static boolean isVersionHigher(String currentVersion, String compareVersion) { + if (currentVersion == null || compareVersion == null) { + return false; + } + + // Split versions into components + String[] current = currentVersion.split("\\."); + String[] compare = compareVersion.split("\\."); + + // Get the length of the shorter version array + int length = Math.min(current.length, compare.length); + + // Compare each component + for (int i = 0; i < length; i++) { + int currentPart = Integer.parseInt(current[i]); + int comparePart = Integer.parseInt(compare[i]); + + if (currentPart > comparePart) { + return true; + } + if (currentPart < comparePart) { + return false; + } + } + + // If all components so far are equal, the longer version is considered higher + return current.length > compare.length; + } } diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template index eded10e7e..a110744a0 100644 --- a/src/main/resources/settings.yml.template +++ b/src/main/resources/settings.yml.template @@ -13,7 +13,7 @@ security: enableLogin: false # set to 'true' to enable login - csrfDisabled: true # set to 'true' to disable CSRF protection (not recommended for production) + csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production) loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1 loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts loginMethod: all # Accepts values like 'all' and 'normal'(only Login with Username/Password), 'oauth2'(only Login with OAuth2) or 'saml2'(only Login with SAML2) @@ -102,7 +102,8 @@ metrics: AutomaticallyGenerated: key: example UUID: example - + appVersion: 0.35.0 + processExecutor: sessionLimit: # Process executor instances limits libreOfficeSessionLimit: 1 diff --git a/test.sh b/test.sh index 7674c6dcd..2ad259054 100644 --- a/test.sh +++ b/test.sh @@ -104,7 +104,7 @@ main() { # run_tests "Stirling-PDF-Security" "./exampleYmlFiles/docker-compose-latest-security.yml" # docker-compose -f "./exampleYmlFiles/docker-compose-latest-security.yml" down - run_tests "Stirling-PDF-Security-Fat" "./exampleYmlFiles/docker-compose-latest-fat-security.yml" + run_tests "Stirling-PDF-Security-Fat" "./exampleYmlFiles/test_cicd.yml" if [ $? -eq 0 ]; then cd cucumber if python -m behave; then