--- /dev/null
+package org.argeo.cms.cli;
+
+import org.argeo.api.cli.CommandsCli;
+
+public class CmsCli extends CommandsCli {
+
+ public CmsCli(String commandName) {
+ super(commandName);
+ addCommand("launch", new StaticCmsLaunch());
+ }
+
+ @Override
+ public String getDescription() {
+ return "Static CMS utilities.";
+ }
+
+ public static void main(String[] args) {
+ mainImpl(new CmsCli("cms"), args);
+ }
+
+}
--- /dev/null
+package org.argeo.cms.cli;
+
+import java.lang.management.ManagementFactory;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.argeo.api.cli.DescribedCommand;
+import org.argeo.cms.runtime.StaticCms;
+
+public class StaticCmsLaunch implements DescribedCommand<String> {
+ private Option dataOption;
+
+ @Override
+ public Options getOptions() {
+ Options options = new Options();
+ dataOption = Option.builder().longOpt("data").hasArg().required()
+ .desc("path to the writable data area (mandatory)").build();
+ options.addOption(dataOption);
+ return options;
+ }
+
+ @Override
+ public String apply(List<String> args) {
+ CommandLine cl = toCommandLine(args);
+ String dataPath = cl.getOptionValue(dataOption);
+
+ Path instancePath = Paths.get(dataPath);
+ System.setProperty("osgi.instance.area", instancePath.toUri().toString());
+ System.setProperty("argeo.http.port", "0");
+
+ StaticCms staticCms = new StaticCms();
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> staticCms.stop(), "Static CMS Shutdown"));
+ staticCms.start();
+
+ long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+ System.out.println("Static CMS available in " + jvmUptime + " ms.");
+
+ staticCms.waitForStop();
+
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Launch a static CMS";
+ }
+
+}
--- /dev/null
+package org.argeo.cms.servlet.httpserver;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.net.httpserver.Authenticator;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpPrincipal;
+
+public class HttpContextServlet extends HttpServlet {
+ private static final long serialVersionUID = 2321612280413662738L;
+
+ private final HttpContext httpContext;
+
+ public HttpContextServlet(HttpContext httpContext) {
+ this.httpContext = httpContext;
+ }
+
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ try (ServletHttpExchange httpExchange = new ServletHttpExchange(httpContext, req, resp)) {
+ Authenticator authenticator = httpContext.getAuthenticator();
+ if (authenticator != null) {
+ Authenticator.Result authenticationResult = authenticator.authenticate(httpExchange);
+ if (authenticationResult instanceof Authenticator.Success) {
+ HttpPrincipal httpPrincipal = ((Authenticator.Success) authenticationResult).getPrincipal();
+ httpExchange.setPrincipal(httpPrincipal);
+ } else if (authenticationResult instanceof Authenticator.Retry) {
+ resp.setStatus(((Authenticator.Retry) authenticationResult).getResponseCode());
+ return;
+ } else if (authenticationResult instanceof Authenticator.Failure) {
+ resp.setStatus(((Authenticator.Failure) authenticationResult).getResponseCode());
+ return;
+ } else {
+ throw new UnsupportedOperationException(
+ "Authentication result " + authenticationResult.getClass().getName() + " is not supported");
+ }
+ }
+
+ HttpHandler httpHandler = httpContext.getHandler();
+ httpHandler.handle(httpExchange);
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.cms.servlet.httpserver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.net.ssl.SSLSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpPrincipal;
+import com.sun.net.httpserver.HttpsExchange;
+
+public class ServletHttpExchange extends HttpsExchange {
+ private final HttpContext httpContext;
+ private final HttpServletRequest httpServletRequest;
+ private final HttpServletResponse httpServletResponse;
+
+ private final Headers requestHeaders;
+ private final Headers responseHeaders;
+
+ private InputStream filteredIn;
+ private OutputStream filteredOut;
+
+ private HttpPrincipal principal;
+
+ public ServletHttpExchange(HttpContext httpContext, HttpServletRequest httpServletRequest,
+ HttpServletResponse httpServletResponse) {
+ this.httpContext = httpContext;
+ this.httpServletRequest = httpServletRequest;
+ this.httpServletResponse = httpServletResponse;
+
+ // request headers
+ requestHeaders = new Headers();
+ for (Enumeration<String> headerNames = httpServletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
+ String headerName = headerNames.nextElement();
+ List<String> values = new ArrayList<>();
+ for (Enumeration<String> headerValues = httpServletRequest.getHeaders(headerName); headerValues
+ .hasMoreElements();)
+ values.add(headerValues.nextElement());
+ requestHeaders.put(headerName, values);
+ }
+
+ responseHeaders = new Headers();
+ }
+
+ @Override
+ public SSLSession getSSLSession() {
+ Object obj = httpServletRequest.getAttribute("javax.net.ssl.session");
+ if (obj == null || !(obj instanceof SSLSession))
+ throw new IllegalStateException("SSL session not found");
+ return (SSLSession) obj;
+ }
+
+ @Override
+ public Headers getRequestHeaders() {
+ return requestHeaders;
+ }
+
+ @Override
+ public Headers getResponseHeaders() {
+ return responseHeaders;
+ }
+
+ @Override
+ public URI getRequestURI() {
+ return URI.create(httpServletRequest.getRequestURI());
+ }
+
+ @Override
+ public String getRequestMethod() {
+ return httpServletRequest.getMethod();
+ }
+
+ @Override
+ public HttpContext getHttpContext() {
+ return httpContext;
+ }
+
+ @Override
+ public void close() {
+ try {
+ httpServletRequest.getInputStream().close();
+ } catch (IOException e) {
+ // TODO use proper logging
+ e.printStackTrace();
+ }
+ try {
+ httpServletResponse.getOutputStream().close();
+ } catch (IOException e) {
+ // TODO use proper logging
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public InputStream getRequestBody() {
+ try {
+ if (filteredIn != null)
+ return filteredIn;
+ else
+ return httpServletRequest.getInputStream();
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot get request body", e);
+ }
+ }
+
+ @Override
+ public OutputStream getResponseBody() {
+ try {
+ if (filteredOut != null)
+ return filteredOut;
+ else
+ return httpServletResponse.getOutputStream();
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot get response body", e);
+ }
+ }
+
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
+ for (String headerName : responseHeaders.keySet()) {
+ for (String headerValue : responseHeaders.get(headerName)) {
+ httpServletResponse.addHeader(headerName, headerValue);
+ }
+ }
+ // TODO deal with content length etc.
+ httpServletResponse.setStatus(rCode);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress() {
+ return new InetSocketAddress(httpServletRequest.getRemoteHost(), httpServletRequest.getRemotePort());
+ }
+
+ @Override
+ public int getResponseCode() {
+ return httpServletResponse.getStatus();
+ }
+
+ @Override
+ public InetSocketAddress getLocalAddress() {
+ return new InetSocketAddress(httpServletRequest.getLocalName(), httpServletRequest.getLocalPort());
+ }
+
+ @Override
+ public String getProtocol() {
+ return httpServletRequest.getProtocol();
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return httpServletRequest.getAttribute(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object value) {
+ httpServletRequest.setAttribute(name, value);
+ }
+
+ @Override
+ public void setStreams(InputStream i, OutputStream o) {
+ if (i != null)
+ filteredIn = i;
+ if (o != null)
+ filteredOut = o;
+
+ }
+
+ @Override
+ public HttpPrincipal getPrincipal() {
+ return principal;
+ }
+
+ void setPrincipal(HttpPrincipal principal) {
+ this.principal = principal;
+ }
+
+}
private static final String INTERNAL_CONTEXT_CLASSLOADER = "org.eclipse.equinox.http.jetty.internal.ContextClassLoader";
@Override
- protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException {
+ protected void addServlets(ServletContextHandler rootContextHandler) throws ServletException {
ServletHolder holder = new ServletHolder(new InternalHttpServiceServlet());
holder.setInitOrder(0);
holder.setInitParameter(Constants.SERVICE_VENDOR, "Eclipse.org"); //$NON-NLS-1$
// holder.setInitParameter(JettyConstants.CONTEXT_PATH,
// httpContext.getContextPath());
- servletContextHandler.addServlet(holder, "/*");
+ rootContextHandler.addServlet(holder, "/*");
// post-start
- SessionHandler sessionManager = servletContextHandler.getSessionHandler();
+ SessionHandler sessionManager = rootContextHandler.getSessionHandler();
sessionManager.addEventListener((HttpSessionIdListener) holder.getServlet());
}
output.. = bin/
bin.includes = META-INF/,\
.
+additional.bundles = org.eclipse.jetty.io,\
+ org.eclipse.jetty.security,\
+ org.argeo.ext.slf4j,\
+ org.slf4j.api
package org.argeo.cms.jetty;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
-import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsState;
import org.argeo.cms.CmsDeployProperty;
import org.argeo.cms.websocket.javax.server.CmsWebSocketConfigurator;
import org.argeo.cms.websocket.javax.server.TestEndpoint;
-import org.eclipse.jetty.http.UriCompliance;
-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.session.SessionHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
-public class CmsJettyServer {
- private static final CmsLog log = CmsLog.getLog(CmsJettyServer.class);
-
- private static final int DEFAULT_IDLE_TIMEOUT = 30000;
+public class CmsJettyServer extends JettyHttpServer {
private static final String CONTEXT_TEMPDIR = "javax.servlet.context.tempdir";
-
// Equinox compatibility
private static final String INTERNAL_CONTEXT_CLASSLOADER = "org.eclipse.equinox.http.jetty.internal.ContextClassLoader";
+// private static final CmsLog log = CmsLog.getLog(CmsJettyServer.class);
- private Server server;
+// private Server server;
+// private Path tempDir;
+//
+// private ServerConnector httpConnector;
+// private ServerConnector httpsConnector;
private Path tempDir;
- private ServerConnector httpConnector;
- private ServerConnector httpsConnector;
-
// WebSocket
private ServerContainer wsServerContainer;
private ServerEndpointConfig.Configurator wsEndpointConfigurator;
private CmsState cmsState;
+ protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException {
+ }
+
+ @Override
public void start() {
try {
tempDir = Files.createTempDirectory("jetty");
-
- server = new Server(new QueuedThreadPool(10, 1));
-
- configure();
- // context.addServlet(new ServletHolder(new RWTServlet()), "/" + entryPoint);
- // Required to serve rwt-resources. It is important that this is last.
-// ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
-// context.addServlet(holderPwd, "/");
-
- if (httpConnector != null) {
- httpConnector.open();
- server.addConnector(httpConnector);
- }
-
- if (httpsConnector != null) {
- httpsConnector.open();
- server.addConnector(httpsConnector);
- }
-
- // holder
-
- // context
- ServletContextHandler httpContext = createHttpContext();
- // httpContext.addServlet(holder, "/*");
- addServlets(httpContext);
- enableWebSocket(httpContext);
- server.setHandler(httpContext);
-
- //
- // START
- server.start();
- //
-
- Runtime.getRuntime().addShutdownHook(new Thread(() -> stop(), "Jetty shutdown"));
-
- log.info(httpPortsMsg());
- } catch (Exception e) {
- throw new IllegalStateException("Cannot start Jetty HTTPS server", e);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot create temp dir", e);
}
+ super.start();
}
- protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException {
- }
+ protected ServletContextHandler createRootContextHandler() {
+ ServletContextHandler servletContextHandler = new ServletContextHandler();
+ servletContextHandler.setAttribute(INTERNAL_CONTEXT_CLASSLOADER,
+ Thread.currentThread().getContextClassLoader());
+ servletContextHandler.setClassLoader(this.getClass().getClassLoader());
+ servletContextHandler.setContextPath("/");
- public Integer getHttpPort() {
- if (httpConnector == null)
- return null;
- return httpConnector.getLocalPort();
- }
-
- public Integer getHttpsPort() {
- if (httpsConnector == null)
- return null;
- return httpsConnector.getLocalPort();
- }
-
- public void stop() {
- try {
- // serverConnector.close();
- server.stop();
- // TODO delete temp dir
- } catch (Exception e) {
- e.printStackTrace();
- }
+ servletContextHandler.setAttribute(CONTEXT_TEMPDIR, tempDir.toAbsolutePath().toFile());
+ SessionHandler handler = new SessionHandler();
+ handler.setMaxInactiveInterval(-1);
+ servletContextHandler.setSessionHandler(handler);
+ return servletContextHandler;
}
- protected void configure() {
- HttpConfiguration http_config = new HttpConfiguration();
-
- String httpPortStr = getFrameworkProp(CmsDeployProperty.HTTP_PORT);
- String httpsPortStr = getFrameworkProp(CmsDeployProperty.HTTPS_PORT);
-
- /// TODO make it more generic
- String httpHost = getFrameworkProp(CmsDeployProperty.HOST);
-// String httpsHost = getFrameworkProp(
-// JettyConfig.JETTY_PROPERTY_PREFIX + CmsHttpConstants.HTTPS_HOST);
-
- // try {
- if (httpPortStr != null || httpsPortStr != null) {
- boolean httpEnabled = httpPortStr != null;
- // props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
- boolean httpsEnabled = httpsPortStr != null;
- // props.put(JettyHttpConstants.HTTPS_ENABLED, httpsEnabled);
- if (httpsEnabled) {
- int httpsPort = Integer.parseInt(httpsPortStr);
- http_config.setSecureScheme("https");
- http_config.setSecurePort(httpsPort);
- }
-
- if (httpEnabled) {
- int httpPort = Integer.parseInt(httpPortStr);
- httpConnector = new ServerConnector(server, new HttpConnectionFactory(http_config));
- httpConnector.setPort(httpPort);
- httpConnector.setHost(httpHost);
- httpConnector.setIdleTimeout(DEFAULT_IDLE_TIMEOUT);
- }
-
- if (httpsEnabled) {
-
- SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
- // sslContextFactory.setKeyStore(KeyS)
-
- sslContextFactory.setKeyStoreType(getFrameworkProp(CmsDeployProperty.SSL_KEYSTORETYPE));
- sslContextFactory.setKeyStorePath(getFrameworkProp(CmsDeployProperty.SSL_KEYSTORE));
- sslContextFactory.setKeyStorePassword(getFrameworkProp(CmsDeployProperty.SSL_PASSWORD));
- // sslContextFactory.setKeyManagerPassword(getFrameworkProp(CmsDeployProperty.SSL_KEYPASSWORD));
- sslContextFactory.setProtocol("TLS");
-
- sslContextFactory.setTrustStoreType(getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORETYPE));
- sslContextFactory.setTrustStorePath(getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORE));
- sslContextFactory.setTrustStorePassword(getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD));
-
- String wantClientAuth = getFrameworkProp(CmsDeployProperty.SSL_WANTCLIENTAUTH);
- if (wantClientAuth != null && wantClientAuth.equals(Boolean.toString(true)))
- sslContextFactory.setWantClientAuth(true);
- String needClientAuth = getFrameworkProp(CmsDeployProperty.SSL_NEEDCLIENTAUTH);
- if (needClientAuth != null && needClientAuth.equals(Boolean.toString(true)))
- sslContextFactory.setNeedClientAuth(true);
-
- // HTTPS Configuration
- HttpConfiguration https_config = new HttpConfiguration(http_config);
- https_config.addCustomizer(new SecureRequestCustomizer());
- https_config.setUriCompliance(UriCompliance.LEGACY);
-
- // HTTPS connector
- httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"),
- new HttpConnectionFactory(https_config));
- int httpsPort = Integer.parseInt(httpsPortStr);
- httpsConnector.setPort(httpsPort);
- httpsConnector.setHost(httpHost);
- }
-
- }
+ @Override
+ protected void configureRootContextHandler(ServletContextHandler servletContextHandler) throws ServletException {
+ addServlets(servletContextHandler);
+ enableWebSocket(servletContextHandler);
}
protected void enableWebSocket(ServletContextHandler servletContextHandler) {
- String webSocketEnabled = getFrameworkProp(CmsDeployProperty.WEBSOCKET_ENABLED);
+ String webSocketEnabled = getDeployProperty(CmsDeployProperty.WEBSOCKET_ENABLED);
// web socket
if (webSocketEnabled != null && webSocketEnabled.equals(Boolean.toString(true))) {
JavaxWebSocketServletContainerInitializer.configure(servletContextHandler, new Configurator() {
}
}
- protected ServletContextHandler createHttpContext() {
- ServletContextHandler httpContext = new ServletContextHandler();
- httpContext.setAttribute(INTERNAL_CONTEXT_CLASSLOADER, Thread.currentThread().getContextClassLoader());
- httpContext.setClassLoader(this.getClass().getClassLoader());
- httpContext.setContextPath("/");
-
- httpContext.setAttribute(CONTEXT_TEMPDIR, tempDir.toAbsolutePath().toFile());
- SessionHandler handler = new SessionHandler();
- handler.setMaxInactiveInterval(-1);
- httpContext.setSessionHandler(handler);
-
- return httpContext;
- }
-
- private String httpPortsMsg() {
-
- return (httpConnector != null ? "HTTP " + getHttpPort() + " " : " ")
- + (httpsConnector != null ? "HTTPS " + getHttpsPort() : "");
- }
-
- private String getFrameworkProp(CmsDeployProperty deployProperty) {
+ protected String getDeployProperty(CmsDeployProperty deployProperty) {
return cmsState.getDeployProperty(deployProperty.getProperty());
}
--- /dev/null
+package org.argeo.cms.jetty;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.argeo.cms.servlet.httpserver.HttpContextServlet;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+
+import com.sun.net.httpserver.Authenticator;
+import com.sun.net.httpserver.Filter;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+/** Trivial implementation of @{HttpContext}. */
+class JettyHttpContext extends HttpContext {
+ private final JettyHttpServer httpServer;
+ private final String path;
+ private final ContextHandler contextHandler;
+ private final ContextAttributes attributes;
+ private final List<Filter> filters = new ArrayList<>();
+
+ private HttpHandler handler;
+ private Authenticator authenticator;
+
+ public JettyHttpContext(JettyHttpServer httpServer, String path) {
+ this.httpServer = httpServer;
+ this.path = path;
+
+ ServletContextHandler servletContextHandler = new ServletContextHandler();
+ servletContextHandler.setContextPath(path);
+ HttpContextServlet servlet = new HttpContextServlet(this);
+ servletContextHandler.addServlet(new ServletHolder(servlet), "/*");
+ contextHandler = servletContextHandler;
+
+ attributes = new ContextAttributes();
+ }
+
+ @Override
+ public HttpHandler getHandler() {
+ return handler;
+ }
+
+ @Override
+ public void setHandler(HttpHandler handler) {
+ if (this.handler != null)
+ throw new IllegalArgumentException("Handler is already set");
+ Objects.requireNonNull(handler);
+ this.handler = handler;
+
+ if (httpServer.isStarted())
+ try {
+ contextHandler.start();
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot start context handler", e);
+ }
+ }
+
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public HttpServer getServer() {
+ return httpServer;
+ }
+
+ @Override
+ public Map<String, Object> getAttributes() {
+ return attributes;
+ }
+
+ @Override
+ public List<Filter> getFilters() {
+ return filters;
+ }
+
+ @Override
+ public Authenticator setAuthenticator(Authenticator auth) {
+ Authenticator previousAuthenticator = authenticator;
+ this.authenticator = auth;
+ return previousAuthenticator;
+ }
+
+ @Override
+ public Authenticator getAuthenticator() {
+ return authenticator;
+ }
+
+ public Handler getContextHandler() {
+ return contextHandler;
+ }
+
+ private class ContextAttributes extends AbstractMap<String, Object> {
+ @Override
+ public Set<Entry<String, Object>> entrySet() {
+ Set<Entry<String, Object>> entries = new HashSet<>();
+ for (Enumeration<String> keys = contextHandler.getAttributeNames(); keys.hasMoreElements();) {
+ entries.add(new ContextAttributeEntry(keys.nextElement()));
+ }
+ return entries;
+ }
+
+ @Override
+ public Object put(String key, Object value) {
+ Object previousValue = get(key);
+ contextHandler.setAttribute(key, value);
+ return previousValue;
+ }
+
+ private class ContextAttributeEntry implements Map.Entry<String, Object> {
+ private final String key;
+
+ public ContextAttributeEntry(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public Object getValue() {
+ return contextHandler.getAttribute(key);
+ }
+
+ @Override
+ public Object setValue(Object value) {
+ Object previousValue = getValue();
+ contextHandler.setAttribute(key, value);
+ return previousValue;
+ }
+
+ }
+ }
+}
--- /dev/null
+package org.argeo.cms.jetty;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import javax.servlet.ServletException;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.CmsDeployProperty;
+import org.argeo.util.http.HttpServerUtils;
+import org.eclipse.jetty.http.UriCompliance;
+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.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.ExecutorThreadPool;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+
+public class JettyHttpServer extends HttpsServer {
+ private final static CmsLog log = CmsLog.getLog(JettyHttpServer.class);
+
+ private static final int DEFAULT_IDLE_TIMEOUT = 30000;
+
+ private Server server;
+
+ protected ServerConnector httpConnector;
+ protected ServerConnector httpsConnector;
+
+ private InetSocketAddress address;
+
+ private ThreadPoolExecutor executor;
+
+ private HttpsConfigurator httpsConfigurator;
+
+ private final Map<String, JettyHttpContext> contexts = new TreeMap<>();
+
+ protected final ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection();
+
+ private boolean started;
+
+ @Override
+ public void bind(InetSocketAddress addr, int backlog) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void start() {
+ try {
+
+ ThreadPool threadPool = null;
+ if (executor != null) {
+ threadPool = new ExecutorThreadPool(executor);
+ } else {
+ // TODO make it configurable
+ threadPool = new QueuedThreadPool(10, 1);
+ }
+
+ server = new Server(threadPool);
+
+ configureConnectors();
+
+ if (httpConnector != null) {
+ httpConnector.open();
+ server.addConnector(httpConnector);
+ }
+
+ if (httpsConnector != null) {
+ httpsConnector.open();
+ server.addConnector(httpsConnector);
+ }
+
+ // holder
+
+ // context
+ ServletContextHandler rootContextHandler = createRootContextHandler();
+ // httpContext.addServlet(holder, "/*");
+ if (rootContextHandler != null)
+ configureRootContextHandler(rootContextHandler);
+// server.setHandler(rootContextHandler);
+
+ ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
+ if (rootContextHandler != null && !contexts.containsKey("/"))
+ contextHandlers.addHandler(rootContextHandler);
+ for (String contextPath : contexts.keySet()) {
+ JettyHttpContext ctx = contexts.get(contextPath);
+ contextHandlers.addHandler(ctx.getContextHandler());
+ }
+
+ server.setHandler(contextHandlerCollection);
+
+ //
+ // START
+ server.start();
+ //
+
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> stop(), "Jetty shutdown"));
+
+ log.info(httpPortsMsg());
+ started = true;
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot start Jetty HTTPS server", e);
+ }
+ }
+
+ @Override
+ public void stop(int delay) {
+ // TODO wait for processing to complete
+ stop();
+
+ }
+
+ public void stop() {
+ try {
+ // serverConnector.close();
+ server.stop();
+ // TODO delete temp dir
+ started = false;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public void setExecutor(Executor executor) {
+ if (!(executor instanceof ThreadPoolExecutor))
+ throw new IllegalArgumentException("Only " + ThreadPoolExecutor.class.getName() + " are supported");
+ this.executor = (ThreadPoolExecutor) executor;
+ }
+
+ @Override
+ public Executor getExecutor() {
+ return executor;
+ }
+
+ @Override
+ public synchronized HttpContext createContext(String path, HttpHandler handler) {
+ HttpContext httpContext = createContext(path);
+ httpContext.setHandler(handler);
+ return httpContext;
+ }
+
+ @Override
+ public synchronized HttpContext createContext(String path) {
+ if (contexts.containsKey(path))
+ throw new IllegalArgumentException("Context " + path + " already exists");
+ JettyHttpContext httpContext = new JettyHttpContext(this, path);
+ contexts.put(path, httpContext);
+
+ contextHandlerCollection.addHandler(httpContext.getContextHandler());
+ return httpContext;
+ }
+
+ @Override
+ public synchronized void removeContext(String path) throws IllegalArgumentException {
+ if (!contexts.containsKey(path))
+ throw new IllegalArgumentException("Context " + path + " does not exist");
+ JettyHttpContext httpContext = contexts.remove(path);
+ // TODO stop handler first?
+ contextHandlerCollection.removeHandler(httpContext.getContextHandler());
+ }
+
+ @Override
+ public synchronized void removeContext(HttpContext context) {
+ removeContext(context.getPath());
+ }
+
+ @Override
+ public InetSocketAddress getAddress() {
+ return address;
+ }
+
+ @Override
+ public void setHttpsConfigurator(HttpsConfigurator config) {
+ this.httpsConfigurator = config;
+ }
+
+ @Override
+ public HttpsConfigurator getHttpsConfigurator() {
+ return httpsConfigurator;
+ }
+
+
+
+ protected void configureConnectors() {
+ HttpConfiguration httpConfiguration = new HttpConfiguration();
+
+ String httpPortStr = getDeployProperty(CmsDeployProperty.HTTP_PORT);
+ String httpsPortStr = getDeployProperty(CmsDeployProperty.HTTPS_PORT);
+
+ /// TODO make it more generic
+ String httpHost = getDeployProperty(CmsDeployProperty.HOST);
+// String httpsHost = getFrameworkProp(
+// JettyConfig.JETTY_PROPERTY_PREFIX + CmsHttpConstants.HTTPS_HOST);
+
+ // try {
+ if (httpPortStr != null || httpsPortStr != null) {
+ boolean httpEnabled = httpPortStr != null;
+ // props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
+ boolean httpsEnabled = httpsPortStr != null;
+ // props.put(JettyHttpConstants.HTTPS_ENABLED, httpsEnabled);
+ if (httpsEnabled) {
+ int httpsPort = Integer.parseInt(httpsPortStr);
+ httpConfiguration.setSecureScheme("https");
+ httpConfiguration.setSecurePort(httpsPort);
+ }
+
+ if (httpEnabled) {
+ int httpPort = Integer.parseInt(httpPortStr);
+ httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
+ httpConnector.setPort(httpPort);
+ httpConnector.setHost(httpHost);
+ httpConnector.setIdleTimeout(DEFAULT_IDLE_TIMEOUT);
+ }
+
+ if (httpsEnabled) {
+
+ SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
+ // sslContextFactory.setKeyStore(KeyS)
+
+ sslContextFactory.setKeyStoreType(getDeployProperty(CmsDeployProperty.SSL_KEYSTORETYPE));
+ sslContextFactory.setKeyStorePath(getDeployProperty(CmsDeployProperty.SSL_KEYSTORE));
+ sslContextFactory.setKeyStorePassword(getDeployProperty(CmsDeployProperty.SSL_PASSWORD));
+ // sslContextFactory.setKeyManagerPassword(getFrameworkProp(CmsDeployProperty.SSL_KEYPASSWORD));
+ sslContextFactory.setProtocol("TLS");
+
+ sslContextFactory.setTrustStoreType(getDeployProperty(CmsDeployProperty.SSL_TRUSTSTORETYPE));
+ sslContextFactory.setTrustStorePath(getDeployProperty(CmsDeployProperty.SSL_TRUSTSTORE));
+ sslContextFactory.setTrustStorePassword(getDeployProperty(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD));
+
+ String wantClientAuth = getDeployProperty(CmsDeployProperty.SSL_WANTCLIENTAUTH);
+ if (wantClientAuth != null && wantClientAuth.equals(Boolean.toString(true)))
+ sslContextFactory.setWantClientAuth(true);
+ String needClientAuth = getDeployProperty(CmsDeployProperty.SSL_NEEDCLIENTAUTH);
+ if (needClientAuth != null && needClientAuth.equals(Boolean.toString(true)))
+ sslContextFactory.setNeedClientAuth(true);
+
+ // HTTPS Configuration
+ HttpConfiguration https_config = new HttpConfiguration(httpConfiguration);
+ https_config.addCustomizer(new SecureRequestCustomizer());
+ https_config.setUriCompliance(UriCompliance.LEGACY);
+
+ // HTTPS connector
+ httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"),
+ new HttpConnectionFactory(https_config));
+ int httpsPort = Integer.parseInt(httpsPortStr);
+ httpsConnector.setPort(httpsPort);
+ httpsConnector.setHost(httpHost);
+ }
+
+ }
+
+ }
+
+ protected String getDeployProperty(CmsDeployProperty deployProperty) {
+ return System.getProperty(deployProperty.getProperty());
+ }
+
+ private String httpPortsMsg() {
+
+ return (httpConnector != null ? "HTTP " + getHttpPort() + " " : " ")
+ + (httpsConnector != null ? "HTTPS " + getHttpsPort() : "");
+ }
+
+ public Integer getHttpPort() {
+ if (httpConnector == null)
+ return null;
+ return httpConnector.getLocalPort();
+ }
+
+ public Integer getHttpsPort() {
+ if (httpsConnector == null)
+ return null;
+ return httpsConnector.getLocalPort();
+ }
+
+ protected ServletContextHandler createRootContextHandler() {
+ return null;
+ }
+
+ protected void configureRootContextHandler(ServletContextHandler servletContextHandler) throws ServletException {
+
+ }
+
+
+ public boolean isStarted() {
+ return started;
+ }
+
+ public static void main(String... args) {
+ JettyHttpServer httpServer = new JettyHttpServer();
+ System.setProperty("argeo.http.port", "8080");
+ httpServer.createContext("/", (exchange) -> {
+ exchange.getResponseBody().write("Hello World!".getBytes());
+ });
+ httpServer.start();
+ httpServer.createContext("/sub/context", (exchange) -> {
+ final String key = "count";
+ Integer count = (Integer) exchange.getHttpContext().getAttributes().get(key);
+ if (count == null)
+ exchange.getHttpContext().getAttributes().put(key, 0);
+ else
+ exchange.getHttpContext().getAttributes().put(key, count + 1);
+ StringBuilder sb = new StringBuilder();
+ sb.append("Subcontext:");
+ sb.append(" " + key + "=" + exchange.getHttpContext().getAttributes().get(key));
+ sb.append(" relativePath=" + HttpServerUtils.relativize(exchange));
+ exchange.getResponseBody().write(sb.toString().getBytes());
+ });
+ }
+}
Bundle-Activator: org.argeo.cms.internal.osgi.CmsActivator
Import-Package: \
-org.apache.commons.httpclient.cookie;resolution:=optional,\
!com.sun.security.jgss,\
org.osgi.*;version=0.0.0,\
org.apache.xerces.jaxp;resolution:=optional,\
bin.includes = META-INF/,\
.,\
bin/,\
- OSGI-INF/,\
- OSGI-INF/simpleTransactionManager.xml,\
- OSGI-INF/cmsState.xml,\
- OSGI-INF/nodeUserAdmin.xml,\
- OSGI-INF/deployConfig.xml,\
- OSGI-INF/cmsDeployment.xml,\
- OSGI-INF/cmsContext.xml,\
- OSGI-INF/acrContentRepository.xml,\
- OSGI-INF/uuidFactory.xml
+ OSGI-INF/
source.. = src/
-additional.bundles = org.argeo.ext.slf4j,\
- org.slf4j.commons.logging,\
- org.slf4j.api,\
- org.apache.commons.codec
import org.argeo.api.cms.DataAdminPrincipal;
import org.argeo.cms.internal.auth.CmsSessionImpl;
import org.argeo.cms.internal.auth.ImpliedByPrincipal;
-import org.argeo.cms.internal.http.WebCmsSessionImpl;
+import org.argeo.cms.internal.auth.RemoteCmsSessionImpl;
import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.argeo.osgi.useradmin.AuthenticatingUser;
import org.osgi.service.useradmin.Authorization;
currentLocalSession.close();
// new CMS session
UUID cmsSessionUuid = CmsContextImpl.getCmsContext().getUuidFactory().timeUUID();
- cmsSession = new WebCmsSessionImpl(cmsSessionUuid, subject, authorization, locale, request);
+ cmsSession = new RemoteCmsSessionImpl(cmsSessionUuid, subject, authorization, locale, request);
CmsContextImpl.getCmsContext().registerCmsSession(cmsSession);
} else if (!authorization.getName().equals(currentLocalSession.getAuthorization().getName())) {
throw new IllegalStateException("Inconsistent user " + authorization.getName()
} else {
// new CMS session
UUID cmsSessionUuid = CmsContextImpl.getCmsContext().getUuidFactory().timeUUID();
- cmsSession = new WebCmsSessionImpl(cmsSessionUuid, subject, authorization, locale, request);
+ cmsSession = new RemoteCmsSessionImpl(cmsSessionUuid, subject, authorization, locale, request);
CmsContextImpl.getCmsContext().registerCmsSession(cmsSession);
}
--- /dev/null
+package org.argeo.cms.internal.auth;
+
+import java.util.Locale;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+
+import org.argeo.cms.auth.RemoteAuthRequest;
+import org.argeo.cms.auth.RemoteAuthSession;
+import org.osgi.service.useradmin.Authorization;
+
+/** CMS session implementation in a web context. */
+public class RemoteCmsSessionImpl extends CmsSessionImpl {
+ private static final long serialVersionUID = -5178883380637048025L;
+ private RemoteAuthSession httpSession;
+
+ public RemoteCmsSessionImpl(UUID uuid, Subject initialSubject, Authorization authorization, Locale locale,
+ RemoteAuthRequest request) {
+ super(uuid, initialSubject, authorization, locale, request.getSession().getId());
+ httpSession = request.getSession();
+ }
+
+ @Override
+ public boolean isValid() {
+ if (isClosed())
+ return false;
+ return httpSession.isValid();
+ }
+}
--- /dev/null
+package org.argeo.cms.internal.http;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.RemoteAuthCallbackHandler;
+import org.argeo.cms.auth.SpnegoLoginModule;
+
+import com.sun.net.httpserver.Authenticator;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpPrincipal;
+
+public class CmsAuthenticator extends Authenticator {
+ final static String HEADER_AUTHORIZATION = "Authorization";
+ final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
+
+ private final static CmsLog log = CmsLog.getLog(CmsAuthenticator.class);
+
+ // TODO make it configurable
+ private final String httpAuthRealm = "Argeo";
+ private final boolean forceBasic = false;
+
+ @Override
+ public Result authenticate(HttpExchange exch) {
+// if (log.isTraceEnabled())
+// HttpUtils.logRequestHeaders(log, request);
+ RemoteAuthHttpExchange remoteAuthHttpExchange = new RemoteAuthHttpExchange(exch);
+ ClassLoader currentThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(CmsAuthenticator.class.getClassLoader());
+ LoginContext lc;
+ try {
+ lc = CmsAuth.USER
+ .newLoginContext(new RemoteAuthCallbackHandler(remoteAuthHttpExchange, remoteAuthHttpExchange));
+ lc.login();
+ } catch (LoginException e) {
+ // FIXME better analyse failure so as not to try endlessly
+ if (authIsRequired(exch)) {
+ return askForWwwAuth(exch);
+ } else {
+ lc = processUnauthorized(exch);
+// if (log.isTraceEnabled())
+// HttpUtils.logResponseHeaders(log, response);
+ }
+ if (lc == null)
+ return new Authenticator.Failure(403);
+ } finally {
+ Thread.currentThread().setContextClassLoader(currentThreadContextClassLoader);
+ }
+
+ Subject subject = lc.getSubject();
+
+// Subject.doAs(subject, new PrivilegedAction<Void>() {
+//
+// @Override
+// public Void run() {
+// // TODO also set login context in order to log out ?
+// RemoteAuthUtils.configureRequestSecurity(new ServletHttpRequest(request));
+// return null;
+// }
+//
+// });
+ String username = CurrentUser.getUsername(subject);
+ HttpPrincipal httpPrincipal = new HttpPrincipal(username, httpAuthRealm);
+ return new Authenticator.Success(httpPrincipal);
+ }
+
+ protected boolean authIsRequired(HttpExchange httpExchange) {
+ return false;
+ }
+
+ protected LoginContext processUnauthorized(HttpExchange httpExchange) {
+
+ RemoteAuthHttpExchange remoteAuthExchange = new RemoteAuthHttpExchange(httpExchange);
+ // anonymous
+ ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(CmsAuthenticator.class.getClassLoader());
+ LoginContext lc = CmsAuth.ANONYMOUS
+ .newLoginContext(new RemoteAuthCallbackHandler(remoteAuthExchange, remoteAuthExchange));
+ lc.login();
+ return lc;
+ } catch (LoginException e1) {
+ if (log.isDebugEnabled())
+ log.error("Cannot log in as anonymous", e1);
+ return null;
+ } finally {
+ Thread.currentThread().setContextClassLoader(currentContextClassLoader);
+ }
+ }
+
+ protected Authenticator.Retry askForWwwAuth(HttpExchange httpExchange) {
+ // response.setHeader(HttpUtils.HEADER_WWW_AUTHENTICATE, "basic
+ // realm=\"" + httpAuthRealm + "\"");
+ if (SpnegoLoginModule.hasAcceptorCredentials() && !forceBasic)// SPNEGO
+ httpExchange.getResponseHeaders().set(HEADER_WWW_AUTHENTICATE, "Negotiate");
+ else
+ httpExchange.getResponseHeaders().set(HEADER_WWW_AUTHENTICATE, "Basic realm=\"" + httpAuthRealm + "\"");
+
+ // response.setDateHeader("Date", System.currentTimeMillis());
+ // response.setDateHeader("Expires", System.currentTimeMillis() + (24 *
+ // 60 * 60 * 1000));
+ // response.setHeader("Accept-Ranges", "bytes");
+ // response.setHeader("Connection", "Keep-Alive");
+ // response.setHeader("Keep-Alive", "timeout=5, max=97");
+ // response.setContentType("text/html; charset=UTF-8");
+
+ return new Authenticator.Retry(401);
+ }
+
+}
--- /dev/null
+package org.argeo.cms.internal.http;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.argeo.cms.auth.RemoteAuthRequest;
+import org.argeo.cms.auth.RemoteAuthResponse;
+import org.argeo.cms.auth.RemoteAuthSession;
+
+import com.sun.net.httpserver.HttpExchange;
+
+public class RemoteAuthHttpExchange implements RemoteAuthRequest, RemoteAuthResponse {
+ private HttpExchange httpExchange;
+
+ public RemoteAuthHttpExchange(HttpExchange httpExchange) {
+ this.httpExchange = httpExchange;
+ }
+
+ @Override
+ public void setHeader(String keys, String value) {
+ httpExchange.getResponseHeaders().put(keys, Collections.singletonList(value));
+ }
+
+ @Override
+ public RemoteAuthSession getSession() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RemoteAuthSession createSession() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Locale getLocale() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Object getAttribute(String key) {
+ return httpExchange.getAttribute(key);
+ }
+
+ @Override
+ public void setAttribute(String key, Object object) {
+ httpExchange.setAttribute(key, object);
+ }
+
+ @Override
+ public String getHeader(String key) {
+ List<String> lst = httpExchange.getRequestHeaders().get(key);
+ if (lst == null || lst.size() == 0)
+ return null;
+ return lst.get(0);
+ }
+
+ @Override
+ public int getLocalPort() {
+ return httpExchange.getLocalAddress().getPort();
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return httpExchange.getRemoteAddress().getHostName();
+ }
+
+ @Override
+ public int getRemotePort() {
+ return httpExchange.getRemoteAddress().getPort();
+ }
+
+}
+++ /dev/null
-package org.argeo.cms.internal.http;
-
-import java.util.Locale;
-import java.util.UUID;
-
-import javax.security.auth.Subject;
-
-import org.argeo.cms.auth.RemoteAuthRequest;
-import org.argeo.cms.auth.RemoteAuthSession;
-import org.argeo.cms.internal.auth.CmsSessionImpl;
-import org.osgi.service.useradmin.Authorization;
-
-/** CMS session implementation in a web context. */
-public class WebCmsSessionImpl extends CmsSessionImpl {
- private static final long serialVersionUID = -5178883380637048025L;
- private RemoteAuthSession httpSession;
-
- public WebCmsSessionImpl(UUID uuid, Subject initialSubject, Authorization authorization, Locale locale,
- RemoteAuthRequest request) {
- super(uuid, initialSubject, authorization, locale, request.getSession().getId());
- httpSession = request.getSession();
- }
-
- @Override
- public boolean isValid() {
- if (isClosed())
- return false;
- return httpSession.isValid();
- }
-}
+++ /dev/null
-package org.argeo.cms.internal.http.client;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.auth.AuthScheme;
-import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
-import org.apache.commons.httpclient.auth.CredentialsProvider;
-
-/** SPNEGO credential provider */
-public class HttpCredentialProvider implements CredentialsProvider {
-
- @Override
- public Credentials getCredentials(AuthScheme scheme, String host, int port, boolean proxy)
- throws CredentialsNotAvailableException {
- if (scheme instanceof SpnegoAuthScheme)
- return new SpnegoCredentials();
- else
- throw new UnsupportedOperationException("Auth scheme " + scheme.getSchemeName() + " not supported");
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.http.client;
-
-import java.net.URL;
-import java.security.PrivilegedExceptionAction;
-import java.util.ArrayList;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.auth.AuthPolicy;
-import org.apache.commons.httpclient.auth.AuthScheme;
-import org.apache.commons.httpclient.auth.AuthenticationException;
-import org.apache.commons.httpclient.auth.CredentialsProvider;
-import org.apache.commons.httpclient.auth.MalformedChallengeException;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.apache.commons.httpclient.params.DefaultHttpParams;
-import org.apache.commons.httpclient.params.HttpParams;
-import org.argeo.cms.auth.RemoteAuthUtils;
-
-/** Implementation of the SPNEGO auth scheme. */
-public class SpnegoAuthScheme implements AuthScheme {
-// private final static Log log = LogFactory.getLog(SpnegoAuthScheme.class);
-
- public static final String NAME = "Negotiate";
-// private final static Oid KERBEROS_OID;
-// static {
-// try {
-// KERBEROS_OID = new Oid("1.3.6.1.5.5.2");
-// } catch (GSSException e) {
-// throw new IllegalStateException("Cannot create Kerberos OID", e);
-// }
-// }
-
- private final static String DEFAULT_KERBEROS_SERVICE = "HTTP";
-
- private boolean complete = false;
- private String realm;
-
- @Override
- public void processChallenge(String challenge) throws MalformedChallengeException {
- // if(tokenStr!=null){
- // log.error("Received challenge while there is a token. Failing.");
- // complete = false;
- // }
-
- }
-
- @Override
- public String getSchemeName() {
- return NAME;
- }
-
- @Override
- public String getParameter(String name) {
- return null;
- }
-
- @Override
- public String getRealm() {
- return realm;
- }
-
- @Override
- public String getID() {
- return NAME;
- }
-
- @Override
- public boolean isConnectionBased() {
- return true;
- }
-
- @Override
- public boolean isComplete() {
- return complete;
- }
-
- @Override
- public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
- // log.debug("authenticate " + method + " " + uri);
- // return null;
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
-// GSSContext context = null;
- String hostname;
- try {
- hostname = method.getURI().getHost();
- String tokenStr = RemoteAuthUtils.getGssToken(null, DEFAULT_KERBEROS_SERVICE, hostname);
- return "Negotiate " + tokenStr;
- } catch (Exception e1) {
- complete = true;
- throw new AuthenticationException("Cannot authenticate " + method, e1);
- }
-// String serverPrinc = DEFAULT_KERBEROS_SERVICE + "@" + hostname;
-//
-// try {
-// // Get service's principal name
-// GSSManager manager = GSSManager.getInstance();
-// GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE, KERBEROS_OID);
-//
-// // Get the context for authentication
-// context = manager.createContext(serverName, KERBEROS_OID, null, GSSContext.DEFAULT_LIFETIME);
-// // context.requestMutualAuth(true); // Request mutual authentication
-// // context.requestConf(true); // Request confidentiality
-// context.requestCredDeleg(true);
-//
-// byte[] token = new byte[0];
-//
-// // token is ignored on the first call
-// token = context.initSecContext(token, 0, token.length);
-//
-// // Send a token to the server if one was generated by
-// // initSecContext
-// if (token != null) {
-// tokenStr = Base64.getEncoder().encodeToString(token);
-// // complete=true;
-// }
-// } catch (GSSException e) {
-// complete = true;
-// throw new AuthenticationException("Cannot authenticate to " + serverPrinc, e);
-// }
- }
-
- public static void main(String[] args) {
- String principal = System.getProperty("javax.security.auth.login.name");
- if (args.length == 0 || principal == null) {
- System.err.println("usage: java -Djavax.security.auth.login.name=<principal@REALM> "
- + SpnegoAuthScheme.class.getName() + " <url>");
- System.exit(1);
- return;
- }
- String url = args[0];
-
- URL jaasUrl = SpnegoAuthScheme.class.getResource("jaas.cfg");
- System.setProperty("java.security.auth.login.config", jaasUrl.toExternalForm());
- try {
- LoginContext lc = new LoginContext("SINGLE_USER");
- lc.login();
-
- AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class);
- HttpParams params = DefaultHttpParams.getDefaultParams();
- ArrayList<String> schemes = new ArrayList<>();
- schemes.add(SpnegoAuthScheme.NAME);
- params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
- params.setParameter(CredentialsProvider.PROVIDER, new HttpCredentialProvider());
-
- int responseCode = Subject.doAs(lc.getSubject(), new PrivilegedExceptionAction<Integer>() {
- public Integer run() throws Exception {
- HttpClient httpClient = new HttpClient();
- return httpClient.executeMethod(new GetMethod(url));
- }
- });
- System.out.println("Reponse code: " + responseCode);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-}
+++ /dev/null
-package org.argeo.cms.internal.http.client;
-
-import org.apache.commons.httpclient.Credentials;
-
-public class SpnegoCredentials implements Credentials {
-
-}
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
-import org.apache.commons.httpclient.auth.AuthPolicy;
-import org.apache.commons.httpclient.auth.CredentialsProvider;
-import org.apache.commons.httpclient.params.DefaultHttpParams;
-import org.apache.commons.httpclient.params.HttpMethodParams;
-import org.apache.commons.httpclient.params.HttpParams;
import org.apache.commons.io.FileUtils;
import org.argeo.api.cms.CmsAuth;
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsState;
import org.argeo.cms.CmsDeployProperty;
-import org.argeo.cms.internal.http.client.HttpCredentialProvider;
-import org.argeo.cms.internal.http.client.SpnegoAuthScheme;
import org.argeo.osgi.useradmin.AggregatingUserAdmin;
import org.argeo.osgi.useradmin.DirectoryUserAdmin;
import org.argeo.osgi.useradmin.UserDirectory;
}
}
- // Register client-side SPNEGO auth scheme
- AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class);
- HttpParams params = DefaultHttpParams.getDefaultParams();
- ArrayList<String> schemes = new ArrayList<>();
- schemes.add(SpnegoAuthScheme.NAME);// SPNEGO preferred
- // schemes.add(AuthPolicy.BASIC);// incompatible with Basic
- params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
- params.setParameter(CredentialsProvider.PROVIDER, new HttpCredentialProvider());
- params.setParameter(HttpMethodParams.COOKIE_POLICY, KernelConstants.COOKIE_POLICY_BROWSER_COMPATIBILITY);
- // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
}
}
+++ /dev/null
-package org.argeo.cms.runtime;
-
-import org.argeo.api.cli.CommandsCli;
-
-public class CmsCli extends CommandsCli {
-
- public CmsCli(String commandName) {
- super(commandName);
- addCommand("launch", new StaticCmsLaunch());
- }
-
- @Override
- public String getDescription() {
- return "Static CMS utilities.";
- }
-
- public static void main(String[] args) {
- mainImpl(new CmsCli("cms"), args);
- }
-
-}
+++ /dev/null
-package org.argeo.cms.runtime;
-
-import java.lang.management.ManagementFactory;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
-import org.argeo.api.cli.DescribedCommand;
-
-public class StaticCmsLaunch implements DescribedCommand<String> {
- private Option dataOption;
-
- @Override
- public Options getOptions() {
- Options options = new Options();
- dataOption = Option.builder().longOpt("data").hasArg().required()
- .desc("path to the writable data area (mandatory)").build();
- options.addOption(dataOption);
- return options;
- }
-
- @Override
- public String apply(List<String> args) {
- CommandLine cl = toCommandLine(args);
- String dataPath = cl.getOptionValue(dataOption);
-
- Path instancePath = Paths.get(dataPath);
- System.setProperty("osgi.instance.area", instancePath.toUri().toString());
- System.setProperty("argeo.http.port", "0");
-
- StaticCms staticCms = new StaticCms();
- Runtime.getRuntime().addShutdownHook(new Thread(() -> staticCms.stop(), "Static CMS Shutdown"));
- staticCms.start();
-
- long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
- System.out.println("Static CMS available in " + jvmUptime + " ms.");
-
- staticCms.waitForStop();
-
- return null;
- }
-
- @Override
- public String getDescription() {
- return "Launch a static CMS";
- }
-
-}
--- /dev/null
+package org.argeo.util.dav;
+
+import java.io.IOException;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+public class DavServerHandler implements HttpHandler {
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ String method = exchange.getRequestMethod();
+ if (DavMethod.PROPFIND.name().equals(method)) {
+ handle(exchange);
+ } else {
+ throw new IllegalArgumentException("Unsupported method " + method);
+ }
+
+ }
+
+ protected DavResponse handlePROPFIND(HttpExchange exchange) {
+ return null;
+ }
+
+}
--- /dev/null
+package org.argeo.util.http;
+
+import java.net.URI;
+import java.util.Objects;
+
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+
+public class HttpServerUtils {
+
+ public static String relativize(HttpContext httpContext, String path) {
+ Objects.requireNonNull(path);
+ if (!path.startsWith(httpContext.getPath()))
+ throw new IllegalArgumentException(path + " does not belong to context" + httpContext.getPath());
+ String relativePath = path.substring(httpContext.getPath().length());
+ // TODO optimise?
+ if (relativePath.startsWith("/"))
+ relativePath = relativePath.substring(1);
+ return relativePath;
+ }
+
+ public static String relativize(HttpExchange exchange) {
+ URI uri = exchange.getRequestURI();
+ HttpContext httpContext = exchange.getHttpContext();
+ return relativize(httpContext, uri.getPath());
+ }
+
+ /** singleton */
+ private HttpServerUtils() {
+
+ }
+}