From f988401a758260e5034add3f195298356c3f4eae Mon Sep 17 00:00:00 2001 From: Sayan Bhattacharya Date: Tue, 13 Jan 2026 11:53:05 +0530 Subject: [PATCH] ACS-10505 Prevent blind ssrf attacks in libre office transformations (#1167) * ACS-10505 modifying libreoffice workdir and user's registryconfig file to block external links * ACS-10505 modifying libreoffice workdir and user's registryconfig file to block external links * ACS-10505 modifying libreoffice workdir and user's registryconfig file to block external links * ACS-10505 modifying libreoffice workdir and user's registryconfig file to block external links * ACS-10505 used a temp office manager to create a temp user profile. * ACS-10505 libreoffice yml corrected * ACS-10505 PMD Fixes * ACS-10505 generating xcu file via script instead of officemanager * ACS-10505 generating xcu file via script instead of officemanager * ACS-10505 generating xcu file via script instead of officemanager * ACS-10505 generating xcu file via script instead of officemanager * ACS-10505 generating xcu file via script instead of officemanager * ACS-10505 generating xcu file via script instead of officemanager * ACS-10505 generating xcu file via script instead of officemanager * ACS-10505 PMD fixes * ACS-10505 PMD fixes * ACS-10505 PMD fixes * ACS-10505 registrymodifications.xcu is predefined * ACS-10505 registrymodifications.xcu is predefined * ACS-10505 registrymodifications.xcu is predefined in resources of libreoffice * ACS-10505 libreoffice path corrected * ACS-10505 header updated and test cases updated * ACS-10505 test cases updated * ACS-10505 removing redundent classes * ACS-10505 removing redundent scripts * ACS-10505 PMD Fixes * ACS-10505 fixing review comments * ACS-10505 fixing unit test cases * ACS-10505 fixing unit test cases * ACS-10505 updated md file and configurations * ACS-10505 fixing test cases * ACS-10505 fixing test cases for /test endpoints * ACS-10505 fixing test cases for LibreOfficeProfileManagerTest * ACS-10505 PMD Fixes * Update engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManager.java Co-authored-by: Damian Ujma <92095156+damianujma@users.noreply.github.com> * ACS-10505 Logger warn fix * ACS-10505 simplifying return statement. * ACS-10505 simplifying return statement. --------- Co-authored-by: Damian Ujma <92095156+damianujma@users.noreply.github.com> --- docs/external-engine-configuration.md | 58 +++--- .../main/resources/application-default.yaml | 2 +- .../transform/base/TransformController.java | 12 +- .../base/src/main/resources/application.yaml | 3 + .../base/LivenessReadinessProbeTest.java | 1 + .../base/TransformControllerTest.java | 9 +- .../patch/LibreOfficeProfileManager.java | 181 ++++++++++++++++++ .../JodConverterSharedInstance.java | 8 +- .../transformers/LibreOfficeTransformer.java | 8 +- .../main/resources/application-default.yaml | 2 +- .../user/registrymodifications.xcu | 10 + .../patch/LibreOfficeProfileManagerTest.java | 103 ++++++++++ 12 files changed, 361 insertions(+), 36 deletions(-) create mode 100644 engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManager.java create mode 100644 engines/libreoffice/src/main/resources/templateProfileDir/user/registrymodifications.xcu create mode 100644 engines/libreoffice/src/test/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManagerTest.java diff --git a/docs/external-engine-configuration.md b/docs/external-engine-configuration.md index 935a042b..bdf70577 100644 --- a/docs/external-engine-configuration.md +++ b/docs/external-engine-configuration.md @@ -5,17 +5,18 @@ In order to configure an external property it needs to be set as ENV property. The following externalized T-engines properties are available: ## Tika -| Property | Description | Default value | -|----------|-------------|---------------| -| SERVER_PORT | T-Engine Port. | 8090 | -| HOSTNAME | T-Engine Name. | t-engine | -| ACTIVEMQ_URL | ActiveMQ URL. | nio://localhost:61616 | -| ACTIVEMQ_USER | ActiveMQ User. | admin | -| ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | -| ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | -| FILE_STORE_URL | T-Engine Port. | http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file | +| Property | Description | Default value | +|----------|------------------------------------------------------------------------------------------------------|---------------| +| SERVER_PORT | T-Engine Port. | 8090 | +| HOSTNAME | T-Engine Name. | t-engine | +| ACTIVEMQ_URL | ActiveMQ URL. | nio://localhost:61616 | +| ACTIVEMQ_USER | ActiveMQ User. | admin | +| ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | +| ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | +| FILE_STORE_URL | T-Engine Port. | http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file | +| TEST_ENDPOINT_ENABLED | Enable /Disable **/test** endpoint | false | PDFBOX_NOTEXTRACTBOOKMARKS_DEFAULT | The default behaviour for notExtractBookmarksText when this request param is omitted from a request. | false | -| TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for receiving async requests. | org.alfresco.transform.engine.tika.acs | +| TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for receiving async requests. | org.alfresco.transform.engine.tika.acs | ## Pdf-renderer @@ -28,6 +29,7 @@ The following externalized T-engines properties are available: | ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | | ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | | FILE_STORE_URL | T-Engine Port. | http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file | +| TEST_ENDPOINT_ENABLED | Enable /Disable **/test** endpoint | false | TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for async requests. | org.alfresco.transform.engine.alfresco-pdf-renderer.acs | | PDFRENDERER_EXE | Path to Pdf-renderer EXE. | /usr/bin/alfresco-pdf-renderer | @@ -41,27 +43,29 @@ The following externalized T-engines properties are available: | ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | | ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | | FILE_STORE_URL | T-Engine Port. | http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file | +| TEST_ENDPOINT_ENABLED | Enable /Disable **/test** endpoint | false | TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for async requests. | org.alfresco.transform.engine.misc.acs | | MISC_PDFBOX_DEFAULT_FONT | Default font used by PdfBox | NotoSans-Regular | | MISC_HTML_COLLAPSE | Html Collasping Option for HTML to TXT transformation | true | ## Libreoffice -| Property | Description | Default value | -|----------|-------------|---------------| -| SERVER_PORT | T-Engine Port | 8090 | -| HOSTNAME | T-Engine Name. | t-engine | -| ACTIVEMQ_URL | ActiveMQ URL. | nio://localhost:61616 | -| ACTIVEMQ_USER | ActiveMQ User. | admin | -| ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | -| ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | +| Property | Description | Default value | +|----------|-------------|--------------------------------------------------------------------------| +| SERVER_PORT | T-Engine Port | 8090 | +| HOSTNAME | T-Engine Name. | t-engine | +| ACTIVEMQ_URL | ActiveMQ URL. | nio://localhost:61616 | +| ACTIVEMQ_USER | ActiveMQ User. | admin | +| ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | +| ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | | FILE_STORE_URL | T-Engine Port. | http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file | -| TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for async requests. | org.alfresco.transform.engine.libreoffice.acs | -| LIBREOFFICE_HOME | Path to LibreOffice_Home. | /opt/libreoffice7.2 | -| LIBREOFFICE_MAX_TASKS_PER_PROCESS | Number of maximum tasks per process. | 200 | -| LIBREOFFICE_TIMEOUT | Timeout value for LibreOffice `execution timeout`, `queue timeout` and `connection timeout`. | 1200000 | -| LIBREOFFICE_PORT_NUMBERS | LibreOffice port. | 8100 | -| LIBREOFFICE_TEMPLATE_PROFILE_DIR | Path to user profile. | | -| LIBREOFFICE_IS_ENABLED | Enables Libreoffice executioner. | true | +| TEST_ENDPOINT_ENABLED | Enable /Disable **/test** endpoint | false +| TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for async requests. | org.alfresco.transform.engine.libreoffice.acs | +| LIBREOFFICE_HOME | Path to LibreOffice_Home. | /opt/libreoffice7.2 | +| LIBREOFFICE_MAX_TASKS_PER_PROCESS | Number of maximum tasks per process. | 200 | +| LIBREOFFICE_TIMEOUT | Timeout value for LibreOffice `execution timeout`, `queue timeout` and `connection timeout`. | 1200000 | +| LIBREOFFICE_PORT_NUMBERS | LibreOffice port. | 8100 | +| LIBREOFFICE_TEMPLATE_PROFILE_DIR | Path to user profile. | alfresco_default | +| LIBREOFFICE_IS_ENABLED | Enables Libreoffice executioner. | true | ## Imagemagick | Property | Description | Default value | @@ -73,6 +77,7 @@ The following externalized T-engines properties are available: | ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | | ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | | FILE_STORE_URL | T-Engine Port. | http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file | +| TEST_ENDPOINT_ENABLED | Enable /Disable **/test** endpoint | false | TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for async requests. | org.alfresco.transform.engine.imagemagick.acs | | IMAGEMAGICK_ROOT | Path to Imagemagick Root. | /usr/lib64/ImageMagick-7.0.10 | | IMAGEMAGICK_DYN | Path to Imagemagick DYLD. | /usr/lib64/ImageMagick-7.0.10/lib | @@ -91,6 +96,7 @@ The following externalized T-engines properties are available: | ACTIVEMQ_PASSWORD | ActiveMQ Password. | admin | | ACTIVEMQ_URL_PARAMS | ActiveMQ connection options. | ?jms.watchTopicAdvisories=false | | FILE_STORE_URL | T-Engine Port. | http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file | +| TEST_ENDPOINT_ENABLED | Enable /Disable **/test** endpoint | false | PDFBOX_NOTEXTRACTBOOKMARKS_DEFAULT | The default behaviour for notExtractBookmarksText when this request param is omitted from a request. | false | | TRANSFORM_ENGINE_REQUEST_QUEUE | T-Engine queue used for async requests. | org.alfresco.transform.engine.aio.acs | | PDFRENDERER_EXE | Path to Pdf-renderer EXE. | /usr/bin/alfresco-pdf-renderer | @@ -99,7 +105,7 @@ The following externalized T-engines properties are available: | LIBREOFFICE_MAX_TASKS_PER_PROCESS | Number of maximum tasks per process. | 200 | | LIBREOFFICE_TIMEOUT | Timeout value for LibreOffice `execution timeout`, `queue timeout` and `connection timeout`. | 1200000 | | LIBREOFFICE_PORT_NUMBERS | LibreOffice port. | 8100 | -| LIBREOFFICE_TEMPLATE_PROFILE_DIR | Path to user profile. | | +| LIBREOFFICE_TEMPLATE_PROFILE_DIR | Path to user profile. | alfresco_default | | LIBREOFFICE_IS_ENABLED | Enables Libreoffice executioner. | true | | IMAGEMAGICK_ROOT | Path to Imagemagick Root. | /usr/lib64/ImageMagick-7.0.10 | | IMAGEMAGICK_DYN | Path to Imagemagick DYLD. | /usr/lib64/ImageMagick-7.0.10/lib | diff --git a/engines/aio/src/main/resources/application-default.yaml b/engines/aio/src/main/resources/application-default.yaml index 800a44c6..4780936e 100644 --- a/engines/aio/src/main/resources/application-default.yaml +++ b/engines/aio/src/main/resources/application-default.yaml @@ -10,7 +10,7 @@ transform: maxTasksPerProcess: ${LIBREOFFICE_MAX_TASKS_PER_PROCESS:200} timeout: ${LIBREOFFICE_TIMEOUT:1200000} portNumbers: ${LIBREOFFICE_PORT_NUMBERS:8100} - templateProfileDir: ${LIBREOFFICE_TEMPLATE_PROFILE_DIR:} + templateProfileDir: ${LIBREOFFICE_TEMPLATE_PROFILE_DIR:alfresco_default} isEnabled: ${LIBREOFFICE_IS_ENABLED:true} imagemagick: root: ${IMAGEMAGICK_ROOT:/usr/lib64/ImageMagick-7.1.2} diff --git a/engines/base/src/main/java/org/alfresco/transform/base/TransformController.java b/engines/base/src/main/java/org/alfresco/transform/base/TransformController.java index 16e46133..0fde4fd9 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/TransformController.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/TransformController.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * Copyright (C) 2005 - 2026 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -29,6 +29,7 @@ package org.alfresco.transform.base; import static java.text.MessageFormat.format; import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; @@ -117,6 +118,9 @@ public class TransformController @Value("${container.behind-ingres}") private boolean behindIngres; + @Value("${transform.endpoint.test.enable}") + private boolean enableTestEndpoint; + TransformEngine transformEngine; private final AtomicReference probeTransform = new AtomicReference<>(); @@ -314,6 +318,12 @@ public class TransformController @RequestParam(value = TARGET_MIMETYPE, required = false) String targetMimetype, @RequestParam Map origRequestParameters) { + + if (!enableTestEndpoint) + { + throw new TransformException(FORBIDDEN, "Test endpoint is disabled"); + } + // Remaps request parameters from test.html and hands them off to the normal transform endpoint. // There are name and value parameters which allow dynamic names and values to be used. Map requestParameters = new HashMap<>(); diff --git a/engines/base/src/main/resources/application.yaml b/engines/base/src/main/resources/application.yaml index 95a45302..ac1b4e0a 100644 --- a/engines/base/src/main/resources/application.yaml +++ b/engines/base/src/main/resources/application.yaml @@ -36,6 +36,9 @@ logging: filestore-url: ${FILE_STORE_URL:http://localhost:8099/alfresco/api/-default-/private/sfs/versions/1/file} transform: + endpoint: + test: + enable: ${TEST_ENDPOINT_ENABLED:false} core: version: @project.version@ engine: diff --git a/engines/base/src/test/java/org/alfresco/transform/base/LivenessReadinessProbeTest.java b/engines/base/src/test/java/org/alfresco/transform/base/LivenessReadinessProbeTest.java index 7044c12e..e6a8c089 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/LivenessReadinessProbeTest.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/LivenessReadinessProbeTest.java @@ -52,6 +52,7 @@ public abstract class LivenessReadinessProbeTest return transformCore.withEnv("livenessTransformEnabled", "true") .withEnv("maxTransforms", MAX_TRANSFORMS.toString()) + .withEnv("TRANSFORM_ENDPOINT_TEST_ENABLE", "true") .withNetworkAliases(image) .withExposedPorts(8090) .waitingFor(Wait.forListeningPort()); diff --git a/engines/base/src/test/java/org/alfresco/transform/base/TransformControllerTest.java b/engines/base/src/test/java/org/alfresco/transform/base/TransformControllerTest.java index b9f529db..9798ccaa 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/TransformControllerTest.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/TransformControllerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2022 - 2022 Alfresco Software Limited + * Copyright (C) 2022 - 2026 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -79,6 +79,7 @@ import ch.qos.logback.core.AppenderBase; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import org.codehaus.plexus.util.FileUtils; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.mockito.stubbing.Answer; @@ -139,6 +140,12 @@ public class TransformControllerTest AbstractBaseTest.resetProbeForTesting(transformController.getProbeTransform()); } + @BeforeEach + void setUp() + { + ReflectionTestUtils.setField(transformController, "enableTestEndpoint", true); + } + @Test public void testInitEngine() throws Exception { diff --git a/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManager.java b/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManager.java new file mode 100644 index 00000000..3943f962 --- /dev/null +++ b/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManager.java @@ -0,0 +1,181 @@ +/* + * #%L + * Alfresco Transform Core + * %% + * Copyright (C) 2005 - 2026 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.transform.libreoffice.patch; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +/** + * Manages LibreOffice user profile templates for transformations. + * + * @author Sayan Bhattacharya + */ +public class LibreOfficeProfileManager +{ + private static final Logger LOGGER = LoggerFactory.getLogger(LibreOfficeProfileManager.class); + private static final String DEFAULT_LO_TEMPLATE_PROFILE = "libreoffice_templateProfile"; + private static final String DEFAULT_TEMPLATE_MARKER = "alfresco_default"; + + private final String templateProfileDir; + + public LibreOfficeProfileManager(String templateProfileDir) + { + this.templateProfileDir = templateProfileDir; + } + + public String getEffectiveTemplateProfileDir() + { + if (DEFAULT_TEMPLATE_MARKER.equalsIgnoreCase(templateProfileDir)) + { + return createDefaultTemplateProfileDirFromResource(); + } + else if (StringUtils.isNotBlank(templateProfileDir)) + { + LOGGER.warn("Template profile directory found. Make sure that the BlockUntrustedRefererLinks security setting is set to true in the registrymodifications.xcu file of the LibreOffice profile."); + } + else + { + LOGGER.warn("No template profile directory provided, using default settings. Untrusted referer links are allowed. " + + "It is recommended to set the template profile directory to `{}` to prevent Blind SSRF risks.", DEFAULT_TEMPLATE_MARKER); + } + return templateProfileDir; + } + + private String createDefaultTemplateProfileDirFromResource() + { + try + { + String baseDir = "templateProfileDir"; + Resource[] resources = loadResources("classpath:" + baseDir); + if (ArrayUtils.isEmpty(resources)) + { + return null; + } + + Path tempDir = Files.createTempDirectory(DEFAULT_LO_TEMPLATE_PROFILE); + for (Resource resource : resources) + { + // skip non-readable or empty resources + if (!resource.isReadable() || resource.contentLength() == 0) + { + continue; + } + + String relative = resolveRelativePath(resource, baseDir); + if (StringUtils.isBlank(relative)) + { + continue; + } + copyResource(resource, tempDir.resolve(relative)); + } + return tempDir.toString(); + } + catch (IOException e) + { + LOGGER.warn("Error creating temporary directory for LibreOffice profile. {}", e.getMessage()); + return null; + } + } + + private Resource[] loadResources(String classpathTemplateDir) + { + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources; + try + { + resources = resolver.getResources(classpathTemplateDir + "/**"); + } + catch (Exception e) + { + LOGGER.warn("No resources found for classpath: {}.\n {}", classpathTemplateDir, e.getMessage()); + return new Resource[0]; + } + if (ArrayUtils.isEmpty(resources)) + { + LOGGER.warn("No resources found for classpath: {}", classpathTemplateDir); + } + return resources; + } + + private String resolveRelativePath(Resource resource, String baseDir) + { + try + { + String url = resource.getURL().toString(); + if (!url.contains(baseDir)) + { + LOGGER.warn("Base directory '{}' not found in resource URL '{}'. Skipping.", baseDir, url); + return null; + } + int baseIndex = url.indexOf(baseDir); + String relative = url.substring(baseIndex + baseDir.length() + 1); + + if (relative.isEmpty()) + { + LOGGER.warn("Relative path is empty for resource URL '{}'. Skipping.", url); + return null; + } + return relative; + } + catch (Exception e) + { + LOGGER.warn("Error resolving URL for resource: {}. {}", resource, e.getMessage()); + return null; + } + } + + private void copyResource(Resource resource, Path target) + { + try + { + Files.createDirectories(target.getParent()); + try (InputStream in = resource.getInputStream()) + { + LOGGER.info("Creating temporary libreoffice profile file"); + Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING); + } + } + catch (IOException e) + { + LOGGER.error("Error copying resource to temporary file: {}", e.getMessage()); + } + + } + +} diff --git a/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/JodConverterSharedInstance.java b/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/JodConverterSharedInstance.java index f9fff41f..036cde95 100644 --- a/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/JodConverterSharedInstance.java +++ b/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/JodConverterSharedInstance.java @@ -270,7 +270,7 @@ public class JodConverterSharedInstance implements JodConverter } /* (non-Javadoc) - * + * * @see org.alfresco.repo.content.JodConverter#isAvailable() */ public boolean isAvailable() { @@ -278,7 +278,7 @@ public class JodConverterSharedInstance implements JodConverter } /* (non-Javadoc) - * + * * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @PostConstruct public void afterPropertiesSet() @@ -487,7 +487,7 @@ public class JodConverterSharedInstance implements JodConverter } /* (non-Javadoc) - * + * * @see org.springframework.beans.factory.DisposableBean#destroy() */ @PreDestroy public void destroy() @@ -507,7 +507,7 @@ public class JodConverterSharedInstance implements JodConverter } /* (non-Javadoc) - * + * * @see org.alfresco.repo.content.JodConverterWorker#getOfficeManager() */ @Override public OfficeManager getOfficeManager() diff --git a/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/LibreOfficeTransformer.java b/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/LibreOfficeTransformer.java index 702dba65..700fa9de 100644 --- a/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/LibreOfficeTransformer.java +++ b/engines/libreoffice/src/main/java/org/alfresco/transform/libreoffice/transformers/LibreOfficeTransformer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * Copyright (C) 2005 - 2026 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -53,6 +53,7 @@ import org.alfresco.transform.base.TransformManager; import org.alfresco.transform.base.executors.JavaExecutor; import org.alfresco.transform.base.util.CustomTransformerFileAdaptor; import org.alfresco.transform.exceptions.TransformException; +import org.alfresco.transform.libreoffice.patch.LibreOfficeProfileManager; /** * JavaExecutor implementation for running LibreOffice transformations. It loads the transformation logic in the same JVM (check the {@link JodConverter} implementation). @@ -110,6 +111,9 @@ public class LibreOfficeTransformer implements JavaExecutor, CustomTransformerFi throw new IllegalArgumentException("LibreOfficeTransformer LIBREOFFICE_IS_ENABLED variable must be set to true/false"); } + LibreOfficeProfileManager profileManager = new LibreOfficeProfileManager(templateProfileDir); + String effectiveTemplateProfileDir = profileManager.getEffectiveTemplateProfileDir(); + JodConverterSharedInstance sharedInstance = new JodConverterSharedInstance(); jodconverter = sharedInstance; sharedInstance.setOfficeHome(path); @@ -118,8 +122,8 @@ public class LibreOfficeTransformer implements JavaExecutor, CustomTransformerFi sharedInstance.setTaskQueueTimeout(timeout); sharedInstance.setConnectTimeout(timeout); sharedInstance.setPortNumbers(portNumbers); - sharedInstance.setTemplateProfileDir(templateProfileDir); sharedInstance.setEnabled(isEnabled); + sharedInstance.setTemplateProfileDir(effectiveTemplateProfileDir); sharedInstance.afterPropertiesSet(); } diff --git a/engines/libreoffice/src/main/resources/application-default.yaml b/engines/libreoffice/src/main/resources/application-default.yaml index d408b95b..ecce8616 100644 --- a/engines/libreoffice/src/main/resources/application-default.yaml +++ b/engines/libreoffice/src/main/resources/application-default.yaml @@ -7,5 +7,5 @@ transform: maxTasksPerProcess: ${LIBREOFFICE_MAX_TASKS_PER_PROCESS:200} timeout: ${LIBREOFFICE_TIMEOUT:1200000} portNumbers: ${LIBREOFFICE_PORT_NUMBERS:8100} - templateProfileDir: ${LIBREOFFICE_TEMPLATE_PROFILE_DIR:} + templateProfileDir: ${LIBREOFFICE_TEMPLATE_PROFILE_DIR:alfresco_default} isEnabled: ${LIBREOFFICE_IS_ENABLED:true} \ No newline at end of file diff --git a/engines/libreoffice/src/main/resources/templateProfileDir/user/registrymodifications.xcu b/engines/libreoffice/src/main/resources/templateProfileDir/user/registrymodifications.xcu new file mode 100644 index 00000000..a47b6442 --- /dev/null +++ b/engines/libreoffice/src/main/resources/templateProfileDir/user/registrymodifications.xcu @@ -0,0 +1,10 @@ + + + + + true + + + diff --git a/engines/libreoffice/src/test/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManagerTest.java b/engines/libreoffice/src/test/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManagerTest.java new file mode 100644 index 00000000..ebc29a71 --- /dev/null +++ b/engines/libreoffice/src/test/java/org/alfresco/transform/libreoffice/patch/LibreOfficeProfileManagerTest.java @@ -0,0 +1,103 @@ +/* + * #%L + * Alfresco Transform Core + * %% + * Copyright (C) 2005 - 2026 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.transform.libreoffice.patch; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Test cases for LibreOfficeProfileManager + * + * @author Sayan Bhattacharya + */ +@RunWith(MockitoJUnitRunner.class) +class LibreOfficeProfileManagerTest +{ + @Test + void testGetEffectiveTemplateProfileDir_WithNullInput() + { + LibreOfficeProfileManager profileManager = new LibreOfficeProfileManager(null); + String result = profileManager.getEffectiveTemplateProfileDir(); + assertNull(result); + } + + @Test + void testGetEffectiveTemplateProfileDir_WithEmptyString() + { + LibreOfficeProfileManager profileManager = new LibreOfficeProfileManager(""); + String result = profileManager.getEffectiveTemplateProfileDir(); + assertEquals("", result); + } + + @Test + void testGetEffectiveTemplateProfileDir_WithBlankString() + { + LibreOfficeProfileManager profileManager = new LibreOfficeProfileManager(" "); + String result = profileManager.getEffectiveTemplateProfileDir(); + assertEquals(" ", result); + } + + @Test + void testGetEffectiveTemplateProfileDir_WithValidUserPath() + { + String validPath = "/path/to/template/profile"; + LibreOfficeProfileManager profileManager = new LibreOfficeProfileManager(validPath); + String result = profileManager.getEffectiveTemplateProfileDir(); + assertEquals(validPath, result); + } + + @Test + void testGetEffectiveTemplateProfileDir_WithDefaultMarkerCreatesTempDir() + { + LibreOfficeProfileManager profileManager = new LibreOfficeProfileManager("alfresco_default"); + String result = profileManager.getEffectiveTemplateProfileDir(); + assertNotNull(result); + assertFalse(result.isBlank()); + } + + @Test + void testGetEffectiveTemplateProfileDir_WithMultiplePathFormats() + { + String[] paths = { + "C:\\path\\to\\template", + "/path/to/template", + "/path/to/template/", + "relative/path/to/template" + }; + + for (String path : paths) + { + LibreOfficeProfileManager manager = new LibreOfficeProfileManager(path); + String result = manager.getEffectiveTemplateProfileDir(); + assertEquals(path, result); + } + } +}