// CmsExceptionsChain vjeSystemErrors = new CmsExceptionsChain(e);
// ObjectMapper objectMapper = new ObjectMapper();
// System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(vjeSystemErrors));
+// System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(e));
// e.printStackTrace();
// }
// }
public final static String PARAM_PASSWORD = "password";
private static final long serialVersionUID = 2478080654328751539L;
-// private Gson gson = new GsonBuilder().setPrettyPrinting().create();
private ObjectMapper objectMapper = new ObjectMapper();
@Override
JsonGenerator jg = objectMapper.getFactory().createGenerator(response.getWriter());
jg.writeObject(cmsSessionDescriptor);
-// JsonWriter jsonWriter = gson.newJsonWriter(response.getWriter());
-// jsonWriter.beginObject();
-// // Authorization
-// jsonWriter.name("username").value(authorization.getName());
-// jsonWriter.name("displayName").value(authorization.toString());
-// // Roles
-// jsonWriter.name("roles").beginArray();
-// for (String role : authorization.getRoles())
-// if (!role.equals(authorization.getName()))
-// jsonWriter.value(role);
-// jsonWriter.endArray();
-// // CMS session
-// jsonWriter.name("cmsSession").beginObject();
-// jsonWriter.name("uuid").value(cmsSessionId.getUuid().toString());
-// jsonWriter.endObject();
-//
-// // extensions
-// enrichJson(jsonWriter);
-//
-// jsonWriter.endObject();
-
String redirectTo = redirectTo(request);
if (redirectTo != null)
response.sendRedirect(redirectTo);
--- /dev/null
+package org.argeo.cms.integration;
+
+import static org.argeo.node.NodeConstants.LOGIN_CONTEXT_USER;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.argeo.cms.auth.HttpRequestCallbackHandler;
+import org.osgi.service.http.context.ServletContextHelper;
+
+/** Manages security access to servlets. */
+public class CmsPrivateServletContext extends ServletContextHelper {
+ public final static String LOGIN_PAGE = "argeo.cms.integration.loginPage";
+ public final static String LOGIN_SERVLET = "argeo.cms.integration.loginServlet";
+ private String loginPage;
+ private String loginServlet;
+
+ public void init(Map<String, String> properties) {
+ loginPage = properties.get(LOGIN_PAGE);
+ loginServlet = properties.get(LOGIN_SERVLET);
+ }
+
+ /**
+ * Add the {@link AccessControlContext} as a request attribute, or redirect to
+ * the login page.
+ */
+ @Override
+ public boolean handleSecurity(final HttpServletRequest request, HttpServletResponse response) throws IOException {
+ LoginContext lc = null;
+
+ String pathInfo = request.getPathInfo();
+ String servletPath = request.getServletPath();
+ if ((pathInfo != null && (servletPath + pathInfo).equals(loginPage)) || servletPath.contentEquals(loginServlet))
+ return true;
+ try {
+ lc = new LoginContext(LOGIN_CONTEXT_USER, new HttpRequestCallbackHandler(request, response));
+ lc.login();
+ } catch (LoginException e) {
+ lc = processUnauthorized(request, response);
+ if (lc == null)
+ return false;
+ }
+ Subject.doAs(lc.getSubject(), new PrivilegedAction<Void>() {
+
+ @Override
+ public Void run() {
+ request.setAttribute(REMOTE_USER, AccessController.getContext());
+ return null;
+ }
+
+ });
+
+ return true;
+ }
+
+ protected LoginContext processUnauthorized(HttpServletRequest request, HttpServletResponse response) {
+ try {
+ response.sendRedirect(loginPage);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot redirect to login page", e);
+ }
+ return null;
+ }
+}
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
+import java.security.AccessControlContext;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeType;
+import javax.security.auth.Subject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import org.apache.jackrabbit.api.JackrabbitNode;
import org.apache.jackrabbit.api.JackrabbitValue;
import org.argeo.jcr.JcrUtils;
+import org.osgi.service.http.context.ServletContextHelper;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
protected Session openJcrSession(HttpServletRequest req, HttpServletResponse resp, Repository repository,
String workspace) throws RepositoryException {
- return workspace != null ? repository.login(workspace) : repository.login();
+ AccessControlContext acc = (AccessControlContext) req.getAttribute(ServletContextHelper.REMOTE_USER);
+ Subject subject = Subject.getSubject(acc);
+ try {
+ return Subject.doAs(subject, new PrivilegedExceptionAction<Session>() {
+
+ @Override
+ public Session run() throws RepositoryException {
+ return repository.login(workspace);
+ }
+
+ });
+ } catch (PrivilegedActionException e) {
+ if (e.getException() instanceof RepositoryException)
+ throw (RepositoryException) e.getException();
+ else
+ throw new RuntimeException(e.getException());
+ }
+// return workspace != null ? repository.login(workspace) : repository.login();
}
protected String getWorkspace(HttpServletRequest req) {
import java.security.Principal;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import org.argeo.cms.CmsException;
import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
/**
* A {@link Principal} which has been implied by an {@link Authorization}. If it
* identity is removed, the related {@link ImpliedByPrincipal}s can thus be
* removed.
*/
-public final class ImpliedByPrincipal implements Principal {
+public final class ImpliedByPrincipal implements Principal, Role {
private final LdapName name;
private Set<Principal> causes = new HashSet<Principal>();
+ private int type = Role.ROLE;
+
public ImpliedByPrincipal(String name, Principal userPrincipal) {
try {
this.name = new LdapName(name);
return Collections.enumeration(causes);
}
+ /*
+ * USER ADMIN
+ */
+
+ @Override
+ /** Type of {@link Role}, if known. */
+ public int getType() {
+ return type;
+ }
+
+ @Override
+ /** Not supported for the time being. */
+ public Dictionary<String, Object> getProperties() {
+ throw new UnsupportedOperationException();
+ }
+
+ /*
+ * OBJECT
+ */
+
@Override
public int hashCode() {
return name.hashCode();
+++ /dev/null
-package org.argeo.cms.internal.http;
-
-/** Compatible with Jetty. */
-public interface HttpConstants {
- static final String HTTP_ENABLED = "http.enabled";
- static final String HTTP_PORT = "http.port";
- static final String HTTP_HOST = "http.host";
- static final String HTTPS_ENABLED = "https.enabled";
- static final String HTTPS_HOST = "https.host";
- static final String HTTPS_PORT = "https.port";
- static final String SSL_KEYSTORE = "ssl.keystore";
- static final String SSL_PASSWORD = "ssl.password";
- static final String SSL_KEYPASSWORD = "ssl.keypassword";
- static final String SSL_NEEDCLIENTAUTH = "ssl.needclientauth";
- static final String SSL_WANTCLIENTAUTH = "ssl.wantclientauth";
- static final String SSL_PROTOCOL = "ssl.protocol";
- static final String SSL_ALGORITHM = "ssl.algorithm";
- static final String SSL_KEYSTORETYPE = "ssl.keystoretype";
- static final String JETTY_PROPERTY_PREFIX = "org.eclipse.equinox.http.jetty.";
- // Argeo specific
- static final String WEB_SOCKET_ENABLED = "websocket.enabled";
-
-}
--- /dev/null
+package org.argeo.cms.internal.http;
+
+/** Compatible with Jetty. */
+public interface InternalHttpConstants {
+ static final String HTTP_ENABLED = "http.enabled";
+ static final String HTTP_PORT = "http.port";
+ static final String HTTP_HOST = "http.host";
+ static final String HTTPS_ENABLED = "https.enabled";
+ static final String HTTPS_HOST = "https.host";
+ static final String HTTPS_PORT = "https.port";
+ static final String SSL_KEYSTORE = "ssl.keystore";
+ static final String SSL_PASSWORD = "ssl.password";
+ static final String SSL_KEYPASSWORD = "ssl.keypassword";
+ static final String SSL_NEEDCLIENTAUTH = "ssl.needclientauth";
+ static final String SSL_WANTCLIENTAUTH = "ssl.wantclientauth";
+ static final String SSL_PROTOCOL = "ssl.protocol";
+ static final String SSL_ALGORITHM = "ssl.algorithm";
+ static final String SSL_KEYSTORETYPE = "ssl.keystoretype";
+ static final String JETTY_PROPERTY_PREFIX = "org.eclipse.equinox.http.jetty.";
+ // Argeo specific
+ static final String WEBSOCKET_ENABLED = "websocket.enabled";
+
+}
// private final static String MIX_ETAG = "mix:etag";
private final static String JCR_ETAG = "jcr:etag";
- private final static String JCR_LAST_MODIFIED = "jcr:lastModified";
- private final static String JCR_LAST_MODIFIED_BY = "jcr:lastModifiedBy";
- private final static String JCR_MIXIN_TYPES = "jcr:mixinTypes";
+// private final static String JCR_LAST_MODIFIED = "jcr:lastModified";
+// private final static String JCR_LAST_MODIFIED_BY = "jcr:lastModifiedBy";
+// private final static String JCR_MIXIN_TYPES = "jcr:mixinTypes";
private final static String JCR_DATA = "jcr:data";
private final static String JCR_CONTENT = "jcr:data";
return;
if (log.isTraceEnabled())
log.trace("NODE_REMOVED " + eventPath);
- String parentPath = JcrUtils.parentPath(removeNodePath);
+// String parentPath = JcrUtils.parentPath(removeNodePath);
// session.refresh(true);
// setLastModified(parentPath, event);
// session.save();
import javax.naming.directory.BasicAttributes;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
+import javax.websocket.server.ServerEndpointConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.CmsException;
+import org.argeo.cms.internal.http.InternalHttpConstants;
+import org.argeo.cms.websocket.CmsWebSocketConfigurator;
import org.argeo.naming.AttributesDictionary;
import org.argeo.naming.LdifParser;
import org.argeo.naming.LdifWriter;
.getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, NodeConstants.DEFAULT));
if (!webServerConfig.isEmpty()) {
webServerConfig.put("customizer.class", KernelConstants.CMS_JETTY_CUSTOMIZER_CLASS);
+
+ // TODO centralise with Jetty extender
+ Object webSocketEnabled = webServerConfig.get(InternalHttpConstants.WEBSOCKET_ENABLED);
+ if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
+ bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null);
+ webServerConfig.put(InternalHttpConstants.WEBSOCKET_ENABLED, "true");
+ }
}
int tryCount = 60;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.CmsException;
-import org.argeo.cms.internal.http.HttpConstants;
+import org.argeo.cms.internal.http.InternalHttpConstants;
import org.argeo.cms.internal.jcr.RepoConf;
import org.argeo.node.NodeConstants;
import org.argeo.osgi.useradmin.UserAdminConf;
String httpPort = getFrameworkProp("org.osgi.service.http.port");
String httpsPort = getFrameworkProp("org.osgi.service.http.port.secure");
/// TODO make it more generic
- String httpHost = getFrameworkProp(HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.HTTP_HOST);
- String httpsHost = getFrameworkProp(HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.HTTPS_HOST);
+ String httpHost = getFrameworkProp(InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTP_HOST);
+ String httpsHost = getFrameworkProp(InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.HTTPS_HOST);
String webSocketEnabled = getFrameworkProp(
- HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.WEB_SOCKET_ENABLED);
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.WEBSOCKET_ENABLED);
final Hashtable<String, Object> props = new Hashtable<String, Object>();
// try {
if (httpPort != null || httpsPort != null) {
boolean httpEnabled = httpPort != null;
- props.put(HttpConstants.HTTP_ENABLED, httpEnabled);
+ props.put(InternalHttpConstants.HTTP_ENABLED, httpEnabled);
boolean httpsEnabled = httpsPort != null;
- props.put(HttpConstants.HTTPS_ENABLED, httpsEnabled);
+ props.put(InternalHttpConstants.HTTPS_ENABLED, httpsEnabled);
if (httpEnabled) {
- props.put(HttpConstants.HTTP_PORT, httpPort);
+ props.put(InternalHttpConstants.HTTP_PORT, httpPort);
if (httpHost != null)
- props.put(HttpConstants.HTTP_HOST, httpHost);
+ props.put(InternalHttpConstants.HTTP_HOST, httpHost);
}
if (httpsEnabled) {
- props.put(HttpConstants.HTTPS_PORT, httpsPort);
+ props.put(InternalHttpConstants.HTTPS_PORT, httpsPort);
if (httpsHost != null)
- props.put(HttpConstants.HTTPS_HOST, httpsHost);
+ props.put(InternalHttpConstants.HTTPS_HOST, httpsHost);
// server certificate
Path keyStorePath = KernelUtils.getOsgiInstancePath(KernelConstants.DEFAULT_KEYSTORE_PATH);
String keyStorePassword = getFrameworkProp(
- HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.SSL_PASSWORD);
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_PASSWORD);
if (keyStorePassword == null)
keyStorePassword = "changeit";
if (!Files.exists(keyStorePath))
createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
- props.put(HttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
- props.put(HttpConstants.SSL_KEYSTORE, keyStorePath.toString());
- props.put(HttpConstants.SSL_PASSWORD, keyStorePassword);
+ props.put(InternalHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
+ props.put(InternalHttpConstants.SSL_KEYSTORE, keyStorePath.toString());
+ props.put(InternalHttpConstants.SSL_PASSWORD, keyStorePassword);
// client certificate authentication
String wantClientAuth = getFrameworkProp(
- HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.SSL_WANTCLIENTAUTH);
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_WANTCLIENTAUTH);
if (wantClientAuth != null)
- props.put(HttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(wantClientAuth));
+ props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(wantClientAuth));
String needClientAuth = getFrameworkProp(
- HttpConstants.JETTY_PROPERTY_PREFIX + HttpConstants.SSL_NEEDCLIENTAUTH);
+ InternalHttpConstants.JETTY_PROPERTY_PREFIX + InternalHttpConstants.SSL_NEEDCLIENTAUTH);
if (needClientAuth != null)
- props.put(HttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth));
+ props.put(InternalHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth));
}
// web socket
if (webSocketEnabled != null && webSocketEnabled.equals("true"))
- props.put(HttpConstants.WEB_SOCKET_ENABLED, true);
+ props.put(InternalHttpConstants.WEBSOCKET_ENABLED, true);
props.put(NodeConstants.CN, NodeConstants.DEFAULT);
}
package org.argeo.cms.websocket;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.List;
+import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.servlet.http.HttpSession;
import javax.websocket.Extension;
import org.apache.commons.logging.LogFactory;
import org.argeo.cms.auth.HttpRequestCallbackHandler;
import org.argeo.node.NodeConstants;
+import org.osgi.service.http.context.ServletContextHelper;
/** Customises the initialisation of a new web socket. */
public class CmsWebSocketConfigurator extends Configurator {
lc.login();
if (log.isDebugEnabled())
log.debug("Web socket logged-in as " + lc.getSubject());
- sec.getUserProperties().put(WEBSOCKET_SUBJECT, lc.getSubject());
+ Subject.doAs(lc.getSubject(), new PrivilegedAction<Void>() {
+
+ @Override
+ public Void run() {
+ sec.getUserProperties().put(ServletContextHelper.REMOTE_USER, AccessController.getContext());
+ return null;
+ }
+
+ });
} catch (Exception e) {
rejectResponse(response, e);
}
protected void rejectResponse(HandshakeResponse response, Exception e) {
// violent implementation, as suggested in
// https://stackoverflow.com/questions/21763829/jsr-356-how-to-abort-a-websocket-connection-during-the-handshake
- throw new IllegalStateException("Web socket cannot be authenticated");
+// throw new IllegalStateException("Web socket cannot be authenticated");
}
}
public class CmsJettyCustomizer extends JettyCustomizer {
private BundleContext bc = FrameworkUtil.getBundle(CmsJettyCustomizer.class).getBundleContext();
+ public final static String WEBSOCKET_ENABLED = "websocket.enabled";
+
@Override
public Object customizeContext(Object context, Dictionary<String, ?> settings) {
// WebSocket
- Object webSocketEnabled = settings.get("websocket.enabled");
- if (webSocketEnabled == null) {
- webSocketEnabled = bc.getProperty("org.eclipse.equinox.http.jetty.websocket.enabled");
- }
+ Object webSocketEnabled = settings.get(WEBSOCKET_ENABLED);
if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
ServletContextHandler servletContextHandler = (ServletContextHandler) context;
WebSocketServerContainerInitializer.configure(servletContextHandler, new Configurator() {