diff --git a/opendj-embedded/src/main/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJ.java b/opendj-embedded/src/main/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJ.java index fc888900a0..d2ef00444b 100644 --- a/opendj-embedded/src/main/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJ.java +++ b/opendj-embedded/src/main/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJ.java @@ -11,7 +11,7 @@ * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * - * Copyright 2024 3A Systems LLC. + * Copyright 2024-2026 3A Systems LLC. */ package org.openidentityplatform.opendj.embedded; @@ -60,6 +60,9 @@ public class EmbeddedOpenDJ implements Runnable, Closeable { final Config config; + private final File instanceDirectory; + private final File rootDirectory; + public EmbeddedOpenDJ() { this(new Config()); } @@ -69,13 +72,18 @@ public EmbeddedOpenDJ(Config config) { logger.info("Create embedded OpenDJ instance: {}", config); this.config = config; - File tempDirectory = new File(System.getProperty("java.io.tmpdir")); - File rootDirectory = new File(tempDirectory, "opendj"); + File instanceDirectory = null; try { - if(rootDirectory.exists()) { - FileUtils.deleteDirectory(rootDirectory); - } + // Create a fresh per-instance parent directory, only accessible by the + // current user, instead of the fixed shared {java.io.tmpdir}/opendj + // directory. The server root inside it must be named "opendj" because + // setup from an archive requires the server root directory to match the + // root directory contained in the archive. The whole directory is + // deleted on close(). + instanceDirectory = Files.createTempDirectory("opendj").toFile(); + File rootDirectory = new File(instanceDirectory, "opendj"); rootDirectory.mkdir(); + logger.info("OpenDJ server root: {}", rootDirectory); File configDirectory = new File(rootDirectory, "config"); File schemaDirectory = new File(configDirectory, "schema"); @@ -109,13 +117,25 @@ public EmbeddedOpenDJ(Config config) { copyFilesFromJar(schemaFiles, JAR_SCHEMA_DIRECTORY, schemaDirectory); + this.instanceDirectory = instanceDirectory; + this.rootDirectory = rootDirectory; }catch (Exception e) { logger.error("Error initializing OpenDJ"); + FileUtils.deleteQuietly(instanceDirectory); throw new RuntimeException(e); } Runtime.getRuntime().addShutdownHook(new Thread(this::close)); } + /** + * Returns the server root directory of this embedded instance. + * + * @return the server root directory + */ + public File getServerRootDirectory() { + return rootDirectory; + } + @Override public void run() { try { @@ -145,13 +165,16 @@ public void run() { @Override public void close() { - if (server.isRunning()) + if (server.isRunning()) { try { logger.info("Shutting down OpenDJ ..."); server.stop(this.getClass().getName(), LocalizableMessage.raw("Stopped after receiving Control-C")); }catch (Throwable e) { logger.error("Error stopping OpenDJ", e); } + } + // close() is also registered as a shutdown hook, so deletion must stay idempotent + FileUtils.deleteQuietly(instanceDirectory); } private void copyFilesFromJar(List jarFiles, String jarDirectory, File outputDirectory) throws IOException{ diff --git a/opendj-embedded/src/test/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJTest.java b/opendj-embedded/src/test/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJTest.java index ece5c5c3e0..d776d1e45e 100644 --- a/opendj-embedded/src/test/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJTest.java +++ b/opendj-embedded/src/test/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJTest.java @@ -11,7 +11,7 @@ * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * - * Copyright 2024 3A Systems LLC. + * Copyright 2024-2026 3A Systems LLC. */ package org.openidentityplatform.opendj.embedded; @@ -54,6 +54,9 @@ public void testOpenDJ() throws Exception { embeddedOpenDJ.run(); assertTrue(embeddedOpenDJ.isRunning()); + File serverRoot = embeddedOpenDJ.getServerRootDirectory(); + assertTrue(serverRoot.isDirectory()); + //import ldif data from an input stream URI resUri = getClass().getClassLoader().getResource("opendj/data.ldif").toURI(); byte[] bytes = Files.readAllBytes(Paths.get(resUri)); @@ -83,5 +86,9 @@ public void testOpenDJ() throws Exception { //stop OpenDJ embeddedOpenDJ.close(); assertFalse(embeddedOpenDJ.isRunning()); + + //the per-instance temporary directory is deleted on close + assertFalse(serverRoot.exists()); + assertFalse(serverRoot.getParentFile().exists()); } -} \ No newline at end of file +} diff --git a/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ControlPanelLauncher.java b/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ControlPanelLauncher.java index 153ba6fb79..269a265ba1 100644 --- a/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ControlPanelLauncher.java +++ b/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ControlPanelLauncher.java @@ -13,6 +13,7 @@ * * Copyright 2008-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.guitools.controlpanel; @@ -21,8 +22,8 @@ import static org.opends.messages.AdminToolMessages.*; import static org.opends.messages.ToolMessages.*; -import java.io.File; import java.io.PrintStream; +import java.nio.file.Files; import javax.swing.SwingUtilities; @@ -64,7 +65,7 @@ public static void main(String[] args) { try { ControlPanelLog.initLogFileHandler( - File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX)); + Files.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX).toFile()); } catch (Throwable t) { System.err.println("Unable to initialize log"); t.printStackTrace(); diff --git a/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ui/NewBaseDNPanel.java b/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ui/NewBaseDNPanel.java index 02ff31ed70..176eaf553d 100644 --- a/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ui/NewBaseDNPanel.java +++ b/opendj-server-legacy/src/main/java/org/opends/guitools/controlpanel/ui/NewBaseDNPanel.java @@ -13,6 +13,7 @@ * * Copyright 2008-2009 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.guitools.controlpanel.ui; @@ -28,6 +29,7 @@ import java.awt.event.ItemListener; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -1239,7 +1241,7 @@ private String generateLdifFile(final File templateFile) final TemplateFile generator = new TemplateFile(resourceDir.getAbsolutePath(), new Random(0)); generator.parse(templateFile.getAbsolutePath(), Collections.emptyList()); - final File tempFile = File.createTempFile("opendj-control-panel", ".ldif"); + final File tempFile = Files.createTempFile("opendj-control-panel", ".ldif").toFile(); tempFile.deleteOnExit(); final String generatedLdifFilePath = tempFile.getAbsolutePath(); diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java index e17abd2d39..cd418262b6 100644 --- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java +++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java @@ -13,12 +13,14 @@ * * Copyright 2008-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.quicksetup; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; import java.util.Date; import java.text.DateFormat; @@ -51,7 +53,7 @@ public static TempLogFile newTempLogFile(final String prefix) { try { - return new TempLogFile(File.createTempFile(prefix, ".log")); + return new TempLogFile(Files.createTempFile(prefix, ".log").toFile()); } catch (final IOException e) { diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java index 5b2e5e8950..d4615d4040 100644 --- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java +++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java @@ -13,6 +13,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.quicksetup.installer; @@ -33,6 +34,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -272,7 +274,7 @@ public File createBaseEntryTempFile(String baseDn) File ldifFile; try { - ldifFile = File.createTempFile("opendj-base-entry", ".ldif"); + ldifFile = Files.createTempFile("opendj-base-entry", ".ldif").toFile(); ldifFile.deleteOnExit(); } catch (IOException ioe) { diff --git a/opendj-server-legacy/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java b/opendj-server-legacy/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java index 6e548e1e12..5e1e9b4698 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.PrintWriter; +import java.nio.file.Files; import java.util.Collection; import java.util.Date; import java.util.Iterator; @@ -98,7 +99,7 @@ public class ConfigGuideGeneration { * * Properties: * GenerationDir - The directory where the doc is generated - * (default is /var/tmp/[CONFIG_GUIDE_DIR>]) + * (default is a fresh temporary directory, printed on startup) * LdapMapping - Presence means that the LDAP mapping section is to be * generated (default is no) * OpenDJWiki - The URL of the OpenDJ Wiki @@ -112,14 +113,14 @@ public class ConfigGuideGeneration { public static void main(String[] args) { Properties properties = System.getProperties(); generationDir = properties.getProperty("GenerationDir"); - if (generationDir == null) { - // Default dir is prefixed by the system-dependent default temporary dir - generationDir = System.getProperty("java.io.tmpdir") + File.separator + - CONFIG_GUIDE_DIR; - } - // Create new dir if necessary try { - new File(generationDir).mkdir(); + if (generationDir == null) { + // Default dir is a fresh temporary dir, only accessible by the current user + generationDir = Files.createTempDirectory(CONFIG_GUIDE_DIR).toString(); + } else { + // Create new dir if necessary + new File(generationDir).mkdir(); + } } catch (Exception e) { e.printStackTrace(); System.exit(1); diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java index 139076a7e6..8624e84092 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/OnDiskMergeImporter.java @@ -38,6 +38,7 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; @@ -425,7 +426,7 @@ private static boolean isUnderAnyOf(DN dn, List branches) private File exportBranches(List includeBranches, List excludeBranches) throws Exception { - final File migrationFile = File.createTempFile("import-migration-", ".ldif"); + final File migrationFile = Files.createTempFile("import-migration-", ".ldif").toFile(); final LDIFExportConfig exportConfig = new LDIFExportConfig(migrationFile.getAbsolutePath(), ExistingFileBehavior.OVERWRITE); exportConfig.setIncludeBranches(includeBranches); diff --git a/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaFilesWriter.java b/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaFilesWriter.java index 010f88116a..8f74982a87 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaFilesWriter.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/schema/SchemaFilesWriter.java @@ -13,6 +13,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.server.schema; @@ -40,6 +41,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermission; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; @@ -725,7 +727,7 @@ private File writeTempSchemaFile(Schema schema, Collection extraAttri } // Create a temporary file to which we can write the schema entry. - File tempFile = File.createTempFile(schemaFile, "temp"); + File tempFile = Files.createTempFile(schemaFile, "temp").toFile(); LDIFExportConfig exportConfig = new LDIFExportConfig(tempFile.getAbsolutePath(), ExistingFileBehavior.OVERWRITE); @@ -1064,11 +1066,23 @@ private void installSchemaFiles(AlertGenerator alertGenerator, HashMap previousPermissions = posixSupported && installedFile.exists() + ? Files.getPosixFilePermissions(installedFile.toPath()) + : null; Files.copy(tempFile.toPath(), installedFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + if (previousPermissions != null) + { + Files.setPosixFilePermissions(installedFile.toPath(), previousPermissions); + } } } catch (Exception e) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/LDAPAuthenticationHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/LDAPAuthenticationHandler.java index 6fe6ae3c23..a9704c2809 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/tools/LDAPAuthenticationHandler.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/tools/LDAPAuthenticationHandler.java @@ -13,6 +13,7 @@ * * Copyright 2006-2009 Sun Microsystems, Inc. * Portions Copyright 2012-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.server.tools; @@ -28,6 +29,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.file.Files; import java.security.MessageDigest; import java.security.PrivilegedExceptionAction; import java.security.SecureRandom; @@ -1892,7 +1894,7 @@ else if (lowerName.equals(SASL_PROPERTY_REALM)) String configFileName; try { - File tempFile = File.createTempFile("login", "conf"); + File tempFile = Files.createTempFile("login", "conf").toFile(); configFileName = tempFile.getAbsolutePath(); tempFile.deleteOnExit(); try (BufferedWriter w = new BufferedWriter(new FileWriter(tempFile, false))) { diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/dsreplication/ReplicationCliMain.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/dsreplication/ReplicationCliMain.java index fa57322c08..07227ffdd5 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/tools/dsreplication/ReplicationCliMain.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/tools/dsreplication/ReplicationCliMain.java @@ -14,6 +14,7 @@ * Copyright 2007-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. * Portions Copyright 2012 profiq s.r.o. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.server.tools.dsreplication; @@ -46,6 +47,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -364,7 +366,7 @@ public static int mainCLI(String[] args, boolean initializeServer, try { ControlPanelLog.initLogFileHandler( - File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX)); + Files.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX).toFile()); } catch (Throwable t) { System.err.println("Unable to initialize log"); t.printStackTrace(); diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/status/StatusCli.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/status/StatusCli.java index 40ba0d9f69..86a47b9388 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/tools/status/StatusCli.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/tools/status/StatusCli.java @@ -29,10 +29,10 @@ import static org.opends.messages.QuickSetupMessages.INFO_NOT_AVAILABLE_LABEL; import static org.opends.messages.ToolMessages.*; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.nio.file.Files; import java.security.GeneralSecurityException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -166,7 +166,7 @@ public static int mainCLI(String[] args, OutputStream outStream, OutputStream er try { ControlPanelLog.initLogFileHandler( - File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX)); + Files.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX).toFile()); ControlPanelLog.initPackage("org.opends.server.tools.status"); } catch (Throwable t) { System.err.println("Unable to initialize log"); diff --git a/opendj-server-legacy/src/main/java/org/opends/server/util/SetupUtils.java b/opendj-server-legacy/src/main/java/org/opends/server/util/SetupUtils.java index 36cec85f90..f37eaac5f9 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/util/SetupUtils.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/util/SetupUtils.java @@ -13,6 +13,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC. */ package org.opends.server.util; @@ -27,6 +28,7 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; +import java.nio.file.Files; import java.security.KeyStoreException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; @@ -149,7 +151,7 @@ public static File createTemplateFile(Set baseDNs, int numEntries) throws IOException { - File templateFile = File.createTempFile("opendj-install", ".template"); + File templateFile = Files.createTempFile("opendj-install", ".template").toFile(); templateFile.deleteOnExit(); LinkedList lines = new LinkedList<>(); diff --git a/opendj-server-legacy/src/test/java/org/opends/server/util/SetupUtilsTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/util/SetupUtilsTestCase.java new file mode 100644 index 0000000000..ce97a35d0c --- /dev/null +++ b/opendj-server-legacy/src/test/java/org/opends/server/util/SetupUtilsTestCase.java @@ -0,0 +1,65 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2026 3A Systems, LLC. + */ +package org.opends.server.util; + +import static org.testng.Assert.assertEquals; + +import java.io.File; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +import org.testng.SkipException; +import org.testng.annotations.Test; + +/** + * A set of test cases for the SetupUtils class. + */ +public class SetupUtilsTestCase extends UtilTestCase +{ + /** + * Tests that temporary files are created readable and writable only by the + * owner on POSIX file systems. + * + * @throws Exception + * If an unexpected problem occurs. + */ + @Test + public void testCreateTemplateFileIsOwnerAccessOnly() throws Exception + { + if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) + { + throw new SkipException("POSIX file permissions are not supported on this platform"); + } + + File templateFile = SetupUtils.createTemplateFile( + Collections.singleton("dc=example,dc=com"), 1); + try + { + Set permissions = + Files.getPosixFilePermissions(templateFile.toPath()); + assertEquals(permissions, + EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE)); + } + finally + { + templateFile.delete(); + } + } +}