/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.keycloak.testsuite; import io.undertow.Undertow; import io.undertow.Undertow.Builder; import io.undertow.servlet.Servlets; import io.undertow.servlet.api.DefaultServletConfig; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.FilterInfo; import io.undertow.servlet.api.ServletInfo; import org.jboss.logging.Logger; import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher; import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer; import org.jboss.resteasy.spi.ResteasyDeployment; import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.filters.KeycloakSessionServletFilter; import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.testsuite.util.cli.TestsuiteCLI; import org.keycloak.util.JsonSerialization; import org.mvel2.util.Make; import javax.servlet.DispatcherType; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> */ public class KeycloakServer { private static final Logger log = Logger.getLogger(KeycloakServer.class); private boolean sysout = false; public static class KeycloakServerConfig { private String host = "localhost"; private int port = 8081; private int workerThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2) * 8; private String resourcesHome; public String getHost() { return host; } public int getPort() { return port; } public String getResourcesHome() { return resourcesHome; } public void setHost(String host) { this.host = host; } public void setPort(int port) { this.port = port; } public void setResourcesHome(String resourcesHome) { this.resourcesHome = resourcesHome; } public int getWorkerThreads() { return workerThreads; } public void setWorkerThreads(int workerThreads) { this.workerThreads = workerThreads; } } public static <T> T loadJson(InputStream is, Class<T> type) { try { return JsonSerialization.readValue(is, type); } catch (IOException e) { throw new RuntimeException("Failed to parse json", e); } } public static void main(String[] args) throws Throwable { bootstrapKeycloakServer(args); } public static KeycloakServer bootstrapKeycloakServer(String[] args) throws Throwable { File f = new File(System.getProperty("user.home"), ".keycloak-server.properties"); if (f.isFile()) { Properties p = new Properties(); p.load(new FileInputStream(f)); System.getProperties().putAll(p); } KeycloakServerConfig config = new KeycloakServerConfig(); for (int i = 0; i < args.length; i++) { if (args[i].equals("-b")) { config.setHost(args[++i]); } if (args[i].equals("-p")) { config.setPort(Integer.valueOf(args[++i])); } } if (System.getProperty("keycloak.port") != null) { config.setPort(Integer.valueOf(System.getProperty("keycloak.port"))); } if (System.getProperty("keycloak.bind.address") != null) { config.setHost(System.getProperty("keycloak.bind.address")); } if (System.getenv("KEYCLOAK_DEV_PORT") != null) { config.setPort(Integer.valueOf(System.getenv("KEYCLOAK_DEV_PORT"))); } if (System.getProperties().containsKey("resources")) { String resources = System.getProperty("resources"); if (resources == null || resources.equals("") || resources.equals("true")) { if (System.getProperties().containsKey("maven.home")) { resources = System.getProperty("user.dir").replaceFirst("testsuite.integration.*", ""); } else { for (String c : System.getProperty("java.class.path").split(File.pathSeparator)) { if (c.contains(File.separator + "testsuite" + File.separator + "integration")) { resources = c.replaceFirst("testsuite.integration.*", ""); } } } } File dir = new File(resources).getAbsoluteFile(); if (!dir.isDirectory()) { throw new RuntimeException("Invalid base resources directory"); } if (!new File(dir, "themes").isDirectory()) { throw new RuntimeException("Invalid resources forms directory"); } if (!System.getProperties().containsKey("keycloak.theme.dir")) { System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "themes", "src", "main", "resources", "theme").getAbsolutePath()); } else { String foo = System.getProperty("keycloak.theme.dir"); System.out.println(foo); } if (!System.getProperties().containsKey("keycloak.theme.cacheTemplates")) { System.setProperty("keycloak.theme.cacheTemplates", "false"); } if (!System.getProperties().containsKey("keycloak.theme.cacheThemes")) { System.setProperty("keycloak.theme.cacheThemes", "false"); } if (!System.getProperties().containsKey("keycloak.theme.staticMaxAge")) { System.setProperty("keycloak.theme.staticMaxAge", "-1"); } config.setResourcesHome(dir.getAbsolutePath()); } if (System.getProperties().containsKey("undertowWorkerThreads")) { int undertowWorkerThreads = Integer.parseInt(System.getProperty("undertowWorkerThreads")); config.setWorkerThreads(undertowWorkerThreads); } detectNodeName(config); final KeycloakServer keycloak = new KeycloakServer(config); keycloak.sysout = true; keycloak.start(); for (int i = 0; i < args.length; i++) { if (args[i].equals("-import")) { keycloak.importRealm(new FileInputStream(args[++i])); } } if (System.getProperties().containsKey("import")) { keycloak.importRealm(new FileInputStream(System.getProperty("import"))); } Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { keycloak.stop(); } }); if (System.getProperties().containsKey("startTestsuiteCLI")) { new TestsuiteCLI(keycloak).start(); } return keycloak; } private KeycloakServerConfig config; private KeycloakSessionFactory sessionFactory; private UndertowJaxrsServer server; public KeycloakServer() { this(new KeycloakServerConfig()); } public KeycloakServer(KeycloakServerConfig config) { this.config = config; } public KeycloakSessionFactory getSessionFactory() { return sessionFactory; } public UndertowJaxrsServer getServer() { return server; } public KeycloakServerConfig getConfig() { return config; } public void importRealm(InputStream realm) { RealmRepresentation rep = loadJson(realm, RealmRepresentation.class); importRealm(rep); } public void importRealm(RealmRepresentation rep) { KeycloakSession session = sessionFactory.create();; session.getTransactionManager().begin(); try { RealmManager manager = new RealmManager(session); if (rep.getId() != null && manager.getRealm(rep.getId()) != null) { info("Not importing realm " + rep.getRealm() + " realm already exists"); return; } if (manager.getRealmByName(rep.getRealm()) != null) { info("Not importing realm " + rep.getRealm() + " realm already exists"); return; } manager.setContextPath("/auth"); RealmModel realm = manager.importRealm(rep); info("Imported realm " + realm.getName()); session.getTransactionManager().commit(); } finally { session.close(); } } protected void setupDevConfig() { if (System.getProperty("keycloak.createAdminUser", "true").equals("true")) { KeycloakSession session = sessionFactory.create(); try { session.getTransactionManager().begin(); if (new ApplianceBootstrap(session).isNoMasterUser()) { new ApplianceBootstrap(session).createMasterRealmUser("admin", "admin"); } session.getTransactionManager().commit(); } finally { session.close(); } } } public void start() throws Throwable { long start = System.currentTimeMillis(); ResteasyDeployment deployment = new ResteasyDeployment(); deployment.setApplicationClass(KeycloakApplication.class.getName()); Builder builder = Undertow.builder() .addHttpListener(config.getPort(), config.getHost()) .setWorkerThreads(config.getWorkerThreads()) .setIoThreads(config.getWorkerThreads() / 8); server = new UndertowJaxrsServer(); try { server.start(builder); DeploymentInfo di = server.undertowDeployment(deployment, ""); di.setClassLoader(getClass().getClassLoader()); di.setContextPath("/auth"); di.setDeploymentName("Keycloak"); di.setDefaultEncoding("UTF-8"); di.addInitParameter(KeycloakApplication.KEYCLOAK_EMBEDDED, "true"); di.setDefaultServletConfig(new DefaultServletConfig(true)); ServletInfo restEasyDispatcher = Servlets.servlet("Keycloak REST Interface", HttpServlet30Dispatcher.class); restEasyDispatcher.addInitParam("resteasy.servlet.mapping.prefix", "/"); restEasyDispatcher.setAsyncSupported(true); di.addServlet(restEasyDispatcher); FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class); filter.setAsyncSupported(true); di.addFilter(filter); di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST); server.deploy(di); sessionFactory = ((KeycloakApplication) deployment.getApplication()).getSessionFactory(); setupDevConfig(); if (config.getResourcesHome() != null) { info("Loading resources from " + config.getResourcesHome()); } info("Started Keycloak (http://" + config.getHost() + ":" + config.getPort() + "/auth) in " + (System.currentTimeMillis() - start) + " ms\n"); } catch (RuntimeException e) { server.stop(); throw e; } } private void info(String message) { if (sysout) { System.out.println(message); } else { log.info(message); } } public void stop() { sessionFactory.close(); server.stop(); info("Stopped Keycloak"); } private static File file(String... path) { StringBuilder s = new StringBuilder(); for (String p : path) { s.append(File.separator); s.append(p); } return new File(s.toString()); } private static void detectNodeName(KeycloakServerConfig config) { String nodeName = System.getProperty(InfinispanConnectionProvider.JBOSS_NODE_NAME); if (nodeName == null) { // Try to autodetect "jboss.node.name" from the port Map<Integer, String> nodesCfg = new HashMap<>(); nodesCfg.put(8181, "node1"); nodesCfg.put(8182, "node2"); nodeName = nodesCfg.get(config.getPort()); if (nodeName != null) { System.setProperty(InfinispanConnectionProvider.JBOSS_NODE_NAME, nodeName); } } if (nodeName != null) { log.infof("Node name: %s", nodeName); } } }