package net.curisit.securis; import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; import java.net.URI; import java.net.URISyntaxException; import java.util.Properties; import javax.inject.Inject; import javax.inject.Named; import net.curisit.securis.ioc.RequestsModule; import net.curisit.securis.ioc.SecurisModule; import net.curisit.securis.utils.Config; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener; import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.name.Names; import com.google.inject.persist.PersistFilter; import com.google.inject.persist.jpa.JpaPersistModule; public class SeCurisServer { private static final Logger LOG = LogManager.getLogger(SeCurisServer.class); private static final Logger CONSOLE = LogManager.getLogger("console"); private static final String PID_FILE = System.getProperty("user.home") + "/.SeCuris/securis-server.pid"; private static Server server; private static Injector injector = null; @Inject @Named("base-uri") private URI uri; private static void showHelp() { CONSOLE.info("Execute SeCuris server using:"); CONSOLE.info(" $ ./securis-server.sh {start|stop}"); } public static void main(String[] args) throws Exception { String command; if (args.length > 0) { command = args[0].toLowerCase(); } else { command = "start"; } switch (command) { case "start": startServer(); break; case "stop": stopServer(); break; default: showHelp(); System.exit(-1); } } private static void stopServer() { if (!new File(PID_FILE).exists()) { CONSOLE.error("SeCuris server is NOT running or PID file is missing"); System.exit(-3); } try { int pid = Integer.parseInt(FileUtils.readFileToString(new File(PID_FILE))); Runtime.getRuntime().exec("kill -SIGINT " + pid); new File(PID_FILE).delete(); CONSOLE.info("SeCuris server process stopped sucessfully (PID: {})", pid); } catch (NumberFormatException | IOException e) { LOG.error("Error getting SeCuris server process PID from file: {}", PID_FILE); } } private static void startServer() { if (new File(PID_FILE).exists()) { try { CONSOLE.error("SeCuris server is already running with PID: {}", FileUtils.readFileToString(new File(PID_FILE))); } catch (IOException e) { LOG.error("Unexpected error", e); } System.exit(-2); } SecurisModule securisModule = new SecurisModule(); JpaPersistModule jpaPersistModule = new JpaPersistModule("localdb"); Properties props = new Properties(); props.put("javax.persistence.jdbc.password", securisModule.getPassword()); props.put("javax.persistence.jdbc.url", securisModule.getUrl(securisModule.getAppDir())); //LOG.info("BD Url: {} {}", securisModule.getUrl(securisModule.getAppDir()), securisModule.getPassword()); jpaPersistModule.properties(props); injector = Guice.createInjector(securisModule, new RequestsModule(), jpaPersistModule); try { startServer(injector.getInstance(Key.get(URI.class, Names.named("base-uri")))); } catch (SeCurisException e) { CONSOLE.error("Error launching the SeCuris server, {}", e); } } private static void savePID() throws SeCurisException { String runtimeName = ManagementFactory.getRuntimeMXBean().getName(); // runtimeName contains something like: "12345@localhost" String pid = runtimeName.substring(0, runtimeName.indexOf('@')); try { FileUtils.writeStringToFile(new File(PID_FILE), pid); CONSOLE.info("SeCuris server process started sucessfully (PID: {})", pid); } catch (IOException e) { LOG.error("Error saving pid file", e); throw new SeCurisException("Error saving pid file"); } } private static void startServer(URI uri) throws SeCurisException { System.out.println("Starting jetty..."); QueuedThreadPool threadPool = new QueuedThreadPool(); threadPool.setMaxThreads(50); server = new Server(); ServerConnector httpConnector = new ServerConnector(server); httpConnector.setPort(Config.getInt(Config.KEYS.SERVER_PORT, 9080)); httpConnector.setHost(Config.get(Config.KEYS.SERVER_HOSTNAME, "0.0.0.0")); server.addConnector(httpConnector); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); context.addEventListener(injector.getInstance(GuiceResteasyBootstrapServletContextListener.class)); context.setInitParameter("resteasy.role.based.security", "true"); context.setInitParameter("resteasy.providers", DefaultExceptionHandler.class.getName()); context.addFilter(new FilterHolder(injector.getInstance(PersistFilter.class)), "/*", null); ServletHolder sh = new ServletHolder(HttpServletDispatcher.class); sh.setName("resteasy"); context.addServlet(sh, "/*"); ResourceHandler staticResources = new ResourceHandler(); try { staticResources.setBaseResource(Resource.newResource(SeCurisServer.class.getResource("/static").toURI())); } catch (IOException | URISyntaxException e) { LOG.error("Error configuring static resources", e); throw new SeCurisException("Error configuring static resources"); } staticResources.setWelcomeFiles(new String[] { "/main.html" }); context.setHandler(staticResources); ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler(); context.setErrorHandler(errorHandler); ContextHandlerCollection contexts = new ContextHandlerCollection(); contexts.setHandlers(new Handler[] { staticResources, context }); HttpConfiguration http_config = new HttpConfiguration(); http_config.setSecureScheme("https"); http_config.setSecurePort(Config.getInt(Config.KEYS.SERVER_SSL_PORT, 9443)); http_config.setOutputBufferSize(32768); http_config.setSendServerVersion(true); http_config.setSendDateHeader(false); HttpConfiguration https_config = new HttpConfiguration(http_config); https_config.addCustomizer(new SecureRequestCustomizer()); SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(Config.get(Config.KEYS.KEYSTORE_PATH)); sslContextFactory.setKeyStoreType(Config.get(Config.KEYS.KEYSTORE_TYPE, "JKS")); sslContextFactory.setKeyStorePassword(Config.get(Config.KEYS.KEYSTORE_PASSWORD, "")); //sslContextFactory.setCertAlias("1"); // sslContextFactory.setKeyManagerPassword("curist3c"); // sslContextFactory.setTrustStorePath("/Users/rob/.ssh/keys/keystore"); // sslContextFactory.setTrustStorePassword("curist3c"); sslContextFactory.checkKeyStore(); sslContextFactory.setNeedClientAuth(false); ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(https_config)); sslConnector.setPort(Config.getInt(Config.KEYS.SERVER_SSL_PORT, 9443)); sslConnector.setHost(Config.get(Config.KEYS.SERVER_HOSTNAME, "0.0.0.0")); server.addConnector( sslConnector ); server.setHandler(context); server.setStopAtShutdown(true); server.addLifeCycleListener(new ServerStoppedListener()); try { server.start(); savePID(); CONSOLE.info("Server running in: {}", String.format("http://%s:%d", httpConnector.getHost(), httpConnector.getPort())); server.join(); } catch (Exception e) { LOG.error("Error starting SeCurisServer", e); throw new SeCurisException("Error starting SeCurisServer"); } } static class ServerStoppedListener extends AbstractLifeCycleListener { @Override public void lifeCycleStopped(LifeCycle event) { if (new File(PID_FILE).exists()) new File(PID_FILE).delete(); } } }