KEYCLOAK-17000 Adding server tmp directory inside the auth-server home directory

This commit is contained in:
mposolda 2021-03-11 16:28:45 +01:00 committed by Marek Posolda
parent 82fc401298
commit 853a6d7327
10 changed files with 142 additions and 21 deletions

View file

@ -28,6 +28,7 @@ import java.util.List;
import org.eclipse.microprofile.config.spi.ConfigSource; import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSourceProvider; import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.platform.Platform;
import org.keycloak.util.Environment; import org.keycloak.util.Environment;
public class KeycloakConfigSourceProvider implements ConfigSourceProvider { public class KeycloakConfigSourceProvider implements ConfigSourceProvider {
@ -104,7 +105,7 @@ public class KeycloakConfigSourceProvider implements ConfigSourceProvider {
String homeDir = Environment.getHomeDir(); String homeDir = Environment.getHomeDir();
if (homeDir == null) { if (homeDir == null) {
return Paths.get(System.getProperty("java.io.tmpdir"), PersistedConfigSource.KEYCLOAK_PROPERTIES); return Paths.get(Platform.getPlatform().getTmpDirectory().toString(), PersistedConfigSource.KEYCLOAK_PROPERTIES);
} }
return Paths.get(homeDir, "conf", PersistedConfigSource.KEYCLOAK_PROPERTIES); return Paths.get(homeDir, "conf", PersistedConfigSource.KEYCLOAK_PROPERTIES);

View file

@ -17,15 +17,20 @@
package org.keycloak.provider.quarkus; package org.keycloak.provider.quarkus;
import java.io.File;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.jboss.logging.Logger;
import org.keycloak.platform.Platform; import org.keycloak.platform.Platform;
import org.keycloak.platform.PlatformProvider; import org.keycloak.platform.PlatformProvider;
import org.keycloak.util.Environment;
public class QuarkusPlatform implements PlatformProvider { public class QuarkusPlatform implements PlatformProvider {
private static final Logger log = Logger.getLogger(QuarkusPlatform.class);
public static void addInitializationException(Throwable throwable) { public static void addInitializationException(Throwable throwable) {
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform(); QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
platform.addDeferredException(throwable); platform.addDeferredException(throwable);
@ -68,6 +73,7 @@ public class QuarkusPlatform implements PlatformProvider {
private AtomicBoolean started = new AtomicBoolean(false); private AtomicBoolean started = new AtomicBoolean(false);
private List<Throwable> deferredExceptions = new CopyOnWriteArrayList<>(); private List<Throwable> deferredExceptions = new CopyOnWriteArrayList<>();
private File tmpDir;
@Override @Override
public void onStartup(Runnable startupHook) { public void onStartup(Runnable startupHook) {
@ -108,4 +114,29 @@ public class QuarkusPlatform implements PlatformProvider {
return deferredExceptions; return deferredExceptions;
} }
@Override
public File getTmpDirectory() {
if (tmpDir == null) {
String homeDir = Environment.getHomeDir();
File tmpDir;
if (homeDir == null) {
// Should happen just in the unit tests
homeDir = System.getProperty("java.io.tmpdir");
tmpDir = new File(homeDir, "keycloak-quarkus-tmp");
tmpDir.mkdir();
} else {
tmpDir = new File(homeDir, "tmp");
tmpDir.mkdir();
}
if (tmpDir.isDirectory()) {
this.tmpDir = tmpDir;
log.debugf("Using server tmp directory: %s", tmpDir.getAbsolutePath());
} else {
throw new RuntimeException("Temporary directory " + tmpDir.getAbsolutePath() + " does not exists and it was not possible to create it.");
}
}
return tmpDir;
}
} }

View file

@ -5,6 +5,7 @@ import org.jboss.logging.Logger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.common.Version; import org.keycloak.common.Version;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.platform.Platform;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -51,7 +52,7 @@ public class GzipResourceEncodingProviderFactory implements ResourceEncodingProv
return cacheDir; return cacheDir;
} }
File cacheRoot = new File(System.getProperty("java.io.tmpdir"), "kc-gzip-cache"); File cacheRoot = new File(Platform.getPlatform().getTmpDirectory(), "kc-gzip-cache");
File cacheDir = new File(cacheRoot, Version.RESOURCES_VERSION); File cacheDir = new File(cacheRoot, Version.RESOURCES_VERSION);
if (cacheRoot.isDirectory()) { if (cacheRoot.isDirectory()) {

View file

@ -22,6 +22,7 @@ import org.keycloak.exportimport.util.MultipleStepsExportProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.platform.Platform;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
@ -38,10 +39,8 @@ public class DirExportProvider extends MultipleStepsExportProvider {
private final File rootDirectory; private final File rootDirectory;
public DirExportProvider() { public DirExportProvider() {
// Determine system tmp directory // Determine platform tmp directory
String tempDir = System.getProperty("java.io.tmpdir"); this.rootDirectory = new File(Platform.getPlatform().getTmpDirectory(), "keycloak-export");
this.rootDirectory = new File(tempDir + "/keycloak-export");
this.rootDirectory.mkdirs(); this.rootDirectory.mkdirs();
logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath()); logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath());

View file

@ -26,6 +26,7 @@ import org.keycloak.exportimport.util.ImportUtils;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.platform.Platform;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
@ -48,11 +49,8 @@ public class DirImportProvider implements ImportProvider {
private final File rootDirectory; private final File rootDirectory;
public DirImportProvider() { public DirImportProvider() {
// Determine system tmp directory // Determine platform tmp directory
String tempDir = System.getProperty("java.io.tmpdir"); this.rootDirectory = new File(Platform.getPlatform().getTmpDirectory(), "keycloak-export");
// Delete and recreate directory inside tmp
this.rootDirectory = new File(tempDir + "/keycloak-export");
if (!this.rootDirectory .exists()) { if (!this.rootDirectory .exists()) {
throw new IllegalStateException("Directory " + this.rootDirectory + " doesn't exist"); throw new IllegalStateException("Directory " + this.rootDirectory + " doesn't exist");
} }

View file

@ -17,6 +17,8 @@
package org.keycloak.platform; package org.keycloak.platform;
import java.io.File;
public interface PlatformProvider { public interface PlatformProvider {
void onStartup(Runnable runnable); void onStartup(Runnable runnable);
@ -25,4 +27,10 @@ public interface PlatformProvider {
void exit(Throwable cause); void exit(Throwable cause);
/**
* @return tmp directory specific to target platform. Implementation can make sure to create "tmp" directory in case it does not exists.
* The directory should be usually inside the corresponding server directory. In production, it should not be system directory like "/tmp" .
*/
File getTmpDirectory();
} }

View file

@ -7,29 +7,24 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.common.Version; import org.keycloak.common.Version;
import org.keycloak.platform.Platform;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
import org.keycloak.testsuite.utils.io.IOUtil;
import org.keycloak.theme.Theme; import org.keycloak.theme.Theme;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -82,12 +77,39 @@ public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
public void gzipEncoding() throws IOException { public void gzipEncoding() throws IOException {
final String resourcesVersion = testingClient.server().fetch(session -> Version.RESOURCES_VERSION, String.class); final String resourcesVersion = testingClient.server().fetch(session -> Version.RESOURCES_VERSION, String.class);
// This will return true if files did not exists before the test OR they did exists, but were successfully deleted.
// False will be returned just in case that files were exists, but were NOT successfully deleted.
// This can happen in rare case when the file were created before in "tmp" directory by different system user and current user can't delete them
boolean filesNotExistsInTmp = testingClient.server().fetch(session -> {
boolean deleted = true;
File file1 = Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "welcome", "keycloak", "css", "welcome.css.gz").toFile();
if (file1.isFile()) {
deleted = file1.delete();
}
File file2 = Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "js", "keycloak.js.gz").toFile();
if (file2.isFile()) {
deleted = deleted && file2.delete();
}
return deleted;
}, Boolean.class);
assertEncoded(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/resources/" + resourcesVersion + "/welcome/keycloak/css/welcome.css", "body {"); assertEncoded(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/resources/" + resourcesVersion + "/welcome/keycloak/css/welcome.css", "body {");
assertEncoded(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/js/keycloak.js", "function(root, factory)"); assertEncoded(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/js/keycloak.js", "function(root, factory)");
// Check no files exists inside "/tmp" directory. We need to skip this test in the rare case when there are thombstone files created by different user
if (filesNotExistsInTmp) {
testingClient.server().run(session -> {
assertFalse(Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "welcome", "keycloak", "css", "welcome.css.gz").toFile().isFile());
assertFalse(Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "js", "keycloak.js.gz").toFile().isFile());
});
}
testingClient.server().run(session -> { testingClient.server().run(session -> {
assertTrue(Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "welcome", "keycloak", "css", "welcome.css.gz").toFile().isFile()); String serverTmpDir = Platform.getPlatform().getTmpDirectory().toString();
assertTrue(Paths.get(System.getProperty("java.io.tmpdir"), "kc-gzip-cache", resourcesVersion, "js", "keycloak.js.gz").toFile().isFile()); assertTrue(Paths.get(serverTmpDir, "kc-gzip-cache", resourcesVersion, "welcome", "keycloak", "css", "welcome.css.gz").toFile().isFile());
assertTrue(Paths.get(serverTmpDir, "kc-gzip-cache", resourcesVersion, "js", "keycloak.js.gz").toFile().isFile());
}); });
} }

View file

@ -31,6 +31,7 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.platform.Platform;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
@ -284,7 +285,7 @@ public class KeycloakServer {
// we generate a dynamic jboss.server.data.dir and remove it at the end. // we generate a dynamic jboss.server.data.dir and remove it at the end.
try { try {
File tempKeycloakFolder = Files.createTempDirectory("keycloak-server-").toFile(); File tempKeycloakFolder = Platform.getPlatform().getTmpDirectory();
File tmpDataDir = new File(tempKeycloakFolder, "/data"); File tmpDataDir = new File(tempKeycloakFolder, "/data");
if (tmpDataDir.mkdirs()) { if (tmpDataDir.mkdirs()) {

View file

@ -17,10 +17,19 @@
package org.keycloak.testsuite; package org.keycloak.testsuite;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import org.jboss.logging.Logger;
import org.keycloak.platform.PlatformProvider; import org.keycloak.platform.PlatformProvider;
public class TestPlatform implements PlatformProvider { public class TestPlatform implements PlatformProvider {
private static final Logger log = Logger.getLogger(TestPlatform.class);
private File tmpDir;
@Override @Override
public void onStartup(Runnable startupHook) { public void onStartup(Runnable startupHook) {
startupHook.run(); startupHook.run();
@ -35,4 +44,30 @@ public class TestPlatform implements PlatformProvider {
throw new RuntimeException(cause); throw new RuntimeException(cause);
} }
@Override
public File getTmpDirectory() {
if (tmpDir == null) {
String projectBuildDir = System.getProperty("project.build.directory");
File tmpDir;
if (projectBuildDir != null) {
tmpDir = new File(projectBuildDir, "server-tmp");
tmpDir.mkdir();
} else {
try {
tmpDir = Files.createTempDirectory("keycloak-server-").toFile();
tmpDir.deleteOnExit();
} catch (IOException ioe) {
throw new RuntimeException("Could not create temporary directory", ioe);
}
}
if (tmpDir.isDirectory()) {
this.tmpDir = tmpDir;
log.infof("Using server tmp directory: %s", tmpDir.getAbsolutePath());
} else {
throw new RuntimeException("Directory " + tmpDir + " was not created and does not exists");
}
}
return tmpDir;
}
} }

View file

@ -17,13 +17,20 @@
package org.keycloak.provider.wildfly; package org.keycloak.provider.wildfly;
import java.io.File;
import org.jboss.logging.Logger;
import org.keycloak.platform.PlatformProvider; import org.keycloak.platform.PlatformProvider;
import org.keycloak.services.ServicesLogger; import org.keycloak.services.ServicesLogger;
public class WildflyPlatform implements PlatformProvider { public class WildflyPlatform implements PlatformProvider {
private static final Logger log = Logger.getLogger(WildflyPlatform.class);
Runnable shutdownHook; Runnable shutdownHook;
private File tmpDir;
@Override @Override
public void onStartup(Runnable startupHook) { public void onStartup(Runnable startupHook) {
startupHook.run(); startupHook.run();
@ -49,4 +56,22 @@ public class WildflyPlatform implements PlatformProvider {
}.start(); }.start();
} }
@Override
public File getTmpDirectory() {
if (tmpDir == null) {
String tmpDirName = System.getProperty("jboss.server.temp.dir");
if (tmpDirName == null) {
throw new RuntimeException("System property jboss.server.temp.dir not set");
}
File tmpDir = new File(tmpDirName);
if (tmpDir.isDirectory()) {
this.tmpDir = tmpDir;
log.debugf("Using server tmp directory: %s", tmpDir.getAbsolutePath());
} else {
throw new RuntimeException("Wildfly temp directory not exists under path: " + tmpDirName);
}
}
return tmpDir;
}
} }